├── docs ├── static │ ├── css │ │ ├── custom.css │ │ └── common.css │ ├── dark-light │ │ ├── moon.png │ │ ├── sun.png │ │ ├── unchecked.svg │ │ ├── checked.svg │ │ ├── moon.svg │ │ ├── sun.svg │ │ ├── light.css │ │ ├── dark.css │ │ ├── common-dark-light.css │ │ └── dark-mode-toggle.mjs │ └── images │ │ ├── logo_tm.png │ │ ├── logo_tm_full.png │ │ └── logo.svg ├── api-reference │ ├── lwutil.rst │ └── index.rst ├── changelog │ └── index.rst ├── authors │ └── index.rst ├── requirements.txt ├── user-manual │ └── index.rst ├── Makefile ├── make.bat ├── index.rst ├── get-started │ └── index.rst └── conf.py ├── .github ├── FUNDING.yml └── workflows │ ├── build-and-test.yml │ └── release.yml ├── lwutil ├── CMakeLists.txt ├── library.cmake └── src │ ├── lwutil │ └── lwutil.c │ └── include │ └── lwutil │ └── lwutil.h ├── AUTHORS ├── .vscode ├── extensions.json ├── settings.json ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── tests ├── test_full │ ├── cmake.cmake │ └── test_full.c ├── test_main.c ├── test.h ├── CMakeLists.txt └── test.py ├── CMakeLists.txt ├── cmake ├── i686-w64-mingw32-gcc.cmake └── x86_64-w64-mingw32-gcc.cmake ├── .readthedocs.yaml ├── .clang-tidy ├── CHANGELOG.md ├── README.md ├── LICENSE ├── library.json ├── CMakePresets.json ├── .clang-format └── .gitignore /docs/static/css/custom.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['paypal.me/tilz0R'] 4 | -------------------------------------------------------------------------------- /docs/api-reference/lwutil.rst: -------------------------------------------------------------------------------- 1 | .. _api_lwutil: 2 | 3 | LwUTIL 4 | ====== 5 | 6 | .. doxygengroup:: LWUTIL 7 | -------------------------------------------------------------------------------- /docs/static/dark-light/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwutil/HEAD/docs/static/dark-light/moon.png -------------------------------------------------------------------------------- /docs/static/dark-light/sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwutil/HEAD/docs/static/dark-light/sun.png -------------------------------------------------------------------------------- /docs/static/images/logo_tm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwutil/HEAD/docs/static/images/logo_tm.png -------------------------------------------------------------------------------- /lwutil/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/library.cmake) -------------------------------------------------------------------------------- /docs/static/images/logo_tm_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaJerle/lwutil/HEAD/docs/static/images/logo_tm_full.png -------------------------------------------------------------------------------- /docs/changelog/index.rst: -------------------------------------------------------------------------------- 1 | .. _changelof: 2 | 3 | Changelog 4 | ========= 5 | 6 | .. literalinclude:: ../../CHANGELOG.md 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Tilen Majerle 2 | Giuliano Motter 3 | Tilen Majerle -------------------------------------------------------------------------------- /docs/authors/index.rst: -------------------------------------------------------------------------------- 1 | .. _authors: 2 | 3 | Authors 4 | ======= 5 | 6 | List of authors and contributors to the library 7 | 8 | .. literalinclude:: ../../AUTHORS -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools", 4 | "ms-vscode.cmake-tools", 5 | "twxs.cmake", 6 | ] 7 | } -------------------------------------------------------------------------------- /tests/test_full/cmake.cmake: -------------------------------------------------------------------------------- 1 | # CMake include file 2 | 3 | # Add more sources 4 | target_sources(${CMAKE_PROJECT_NAME} PRIVATE 5 | ${CMAKE_CURRENT_LIST_DIR}/test_full.c 6 | ) 7 | -------------------------------------------------------------------------------- /docs/api-reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _api_reference: 2 | 3 | API reference 4 | ============= 5 | 6 | List of all the modules: 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | :glob: 11 | 12 | * 13 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=3.5.1 2 | breathe>=4.9.1 3 | urllib3==1.26.15 4 | docutils==0.16 5 | colorama 6 | sphinx_rtd_theme>=1.0.0 7 | sphinx-tabs 8 | sphinxcontrib-svg2pdfconverter 9 | sphinx-sitemap 10 | -------------------------------------------------------------------------------- /docs/static/dark-light/unchecked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lwutil/lwutil.h" 3 | 4 | extern int test_run(void); 5 | 6 | int 7 | main(void) { 8 | int ret = 0; 9 | printf("Application running\r\n"); 10 | ret = test_run(); 11 | printf("Done\r\n"); 12 | return ret; 13 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | # Setup project 4 | project(LwLibPROJECT) 5 | 6 | if(NOT PROJECT_IS_TOP_LEVEL) 7 | add_subdirectory(lwgps) 8 | else() 9 | set(TEST_CMAKE_FILE_NAME "test_full/cmake.cmake") 10 | add_subdirectory(tests) 11 | endif() 12 | -------------------------------------------------------------------------------- /cmake/i686-w64-mingw32-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | # Some default GCC settings 4 | set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 5 | set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 6 | 7 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 8 | -------------------------------------------------------------------------------- /docs/static/dark-light/checked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cmake/x86_64-w64-mingw32-gcc.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | # Some default GCC settings 4 | set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 5 | set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 6 | 7 | set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "lwevt_types.h": "c", 4 | "lwevt_type.h": "c", 5 | "lwevt.h": "c", 6 | "string.h": "c", 7 | "lwevt_opt.h": "c", 8 | "lwutil.h": "c", 9 | "stddef.h": "c", 10 | "stdint.h": "c" 11 | }, 12 | "esbonio.sphinx.confDir": "" 13 | } -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-22.04 4 | tools: 5 | python: "3.11" 6 | 7 | # Build documentation in the docs/ directory with Sphinx 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | # Python configuration 12 | python: 13 | install: 14 | - requirements: docs/requirements.txt 15 | 16 | formats: 17 | - pdf 18 | - epub 19 | -------------------------------------------------------------------------------- /docs/static/dark-light/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | moon 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | /* 6 | * Full configuration is provided by CMake plugin for vscode, 7 | * that shall be installed by user 8 | */ 9 | "name": "Win32", 10 | "intelliSenseMode": "${default}", 11 | "configurationProvider": "ms-vscode.cmake-tools" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | /* GDB must in be in the PATH environment */ 6 | "name": "(Windows) Launch", 7 | "type": "cppdbg", 8 | "request": "launch", 9 | "program": "${command:cmake.launchTargetPath}", 10 | "args": [], 11 | "stopAtEntry": false, 12 | "cwd": "${fileDirname}", 13 | "environment": [] 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /docs/user-manual/index.rst: -------------------------------------------------------------------------------- 1 | .. _um: 2 | 3 | User manual 4 | =========== 5 | 6 | LwUTIL is an utility library to consists of simple and frequencly used functions or macros in the day-to-day C/C++ development. 7 | 8 | Some of the implementations (list is not exhausted): 9 | 10 | * Static array size calculation 11 | * Min and max calculation 12 | * Bitwise operations 13 | * Load and store in little and big endian format, for easier inter-processor communication 14 | * and others.. 15 | 16 | Check :ref:`api_reference` for detailed list of supported functions -------------------------------------------------------------------------------- /docs/static/dark-light/sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: "*, 3 | -abseil-*, 4 | -altera-*, 5 | -android-*, 6 | -fuchsia-*, 7 | -google-*, 8 | -llvm*, 9 | -modernize-use-trailing-return-type, 10 | -zircon-*, 11 | -readability-else-after-return, 12 | -readability-static-accessed-through-instance, 13 | -readability-avoid-const-params-in-decls, 14 | -cppcoreguidelines-non-private-member-variables-in-classes, 15 | -misc-non-private-member-variables-in-classes, 16 | " 17 | WarningsAsErrors: '' 18 | HeaderFilterRegex: '' 19 | FormatStyle: none -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Develop 4 | 5 | - Fix the platformio library package description 6 | 7 | ## 1.4.0 8 | 9 | - Rework library CMake with removed INTERFACE type 10 | - Add extended loading/storing option 11 | 12 | ## 1.3.0 13 | 14 | - Add `LWUTIL_SET_VALUE_IF_PTR_NOT_NULL` 15 | - Add `LWUTIL_MAP` and `LWUTIL_CONSTRAIN` functions 16 | - Add `LWUTIL_ASZ`, a shortcut from `LWUTIL_ARRAYSIZE` 17 | 18 | ## 1.2.0 19 | 20 | - Split CMakeLists.txt files between library and executable 21 | - Change license year to 2022 22 | - Update code style with astyle 23 | - Add `.clang-format` draft 24 | 25 | ## v1.0.0 26 | 27 | - Initial release 28 | - Support for platform.io -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Windows CMake Build & Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - test 8 | pull_request: 9 | branches: 10 | - develop 11 | 12 | jobs: 13 | build: 14 | runs-on: windows-latest 15 | 16 | steps: 17 | - name: Checkout Repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Install MinGW 21 | run: | 22 | choco install mingw --version=12.2.0 -y 23 | echo "C:\ProgramData\chocolatey\lib\mingw\tools\install\mingw64\bin" >> $GITHUB_PATH 24 | gcc --version 25 | 26 | - name: Run Tests 27 | working-directory: tests 28 | run: | 29 | python test.py 30 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/static/dark-light/light.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | color-scheme: light; /* stylelint-disable-line property-no-unknown */ 19 | 20 | --background-color: rgb(240 240 240); 21 | --text-color: rgb(15 15 15); 22 | --shadow-color: rgb(15 15 15 / 50%); 23 | --accent-color: rgb(240 0 0 / 50%); 24 | } 25 | -------------------------------------------------------------------------------- /tests/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_COMMON_HDR_H 2 | #define TEST_COMMON_HDR_H 3 | 4 | #include 5 | #include 6 | 7 | #define RUN_TEST(x) \ 8 | do { \ 9 | if (!(x)) { \ 10 | printf("Test FAILED on line %u with condition " #x "\r\n", (unsigned)__LINE__); \ 11 | return -1; \ 12 | } \ 13 | } while (0) 14 | #define FLT_IS_EQUAL(x, y) (fabs((double)(x) - (double)(y)) < 0.00001) 15 | #define INT_IS_EQUAL(x, y) ((int)((x) == (y))) 16 | 17 | #endif /* TEST_COMMON_HDR_H */ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C language utility library 2 | 3 | Set of different functions and macros usually used in the various applications. 4 | 5 | ## Features 6 | 7 | - Support for C99 or later 8 | - Support for minimal and maximal values 9 | - Support for absolute values 10 | - Support for unused macros 11 | - Support for storing and loading data from array in little- or big- endian formats 12 | - Support for retrieving number of elements for statically allocated array 13 | - Support for bitwise operations to set, clear, check or toggle bits 14 | - Compilation-time assert feature 15 | - User friendly MIT license 16 | 17 | ## How to use 18 | 19 | Usage is very simply. Add `lwutil.c` file to compilation flag and make sure compiler has access to `lwutil.h` for include paths. 20 | 21 | ## Contribute 22 | 23 | This is a generic library and welcomes different contributions. 24 | We accept issue reports or, even better, pull-requests with bugs or new features. 25 | 26 | Use [c-code-style](https://github.com/MaJerle/c-code-style) rules for coding and keep the same coding consistency. -------------------------------------------------------------------------------- /docs/static/dark-light/dark.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | color-scheme: dark; /* stylelint-disable-line property-no-unknown */ 19 | 20 | --background-color: rgb(15 15 15); 21 | --text-color: rgb(240 240 240); 22 | --shadow-color: rgb(240 240 240 / 50%); 23 | --accent-color: rgb(0 0 240 / 50%); 24 | } 25 | 26 | img { 27 | filter: grayscale(50%); 28 | } 29 | 30 | .icon { 31 | filter: invert(100%); 32 | } 33 | 34 | a { 35 | color: yellow; 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Tilen MAJERLE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LwUTIL", 3 | "version": "1.4.0", 4 | "description": "Lightweight utility functions for daily application development, library that simply works in the project", 5 | "keywords": "utility, utils, functions, macros, bits, bit, bitwise, string, str, integer, float, double, char, unsigned", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/MaJerle/lwutil.git" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Tilen Majerle", 13 | "email": "tilen@majerle.eu", 14 | "url": "https://majerle.eu" 15 | } 16 | ], 17 | "license": "MIT", 18 | "homepage": "https://github.com/MaJerle/lwutil", 19 | "dependencies": {}, 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "export": { 23 | "exclude": [ 24 | ".github", 25 | "dev", 26 | "docs", 27 | "**/.vs", 28 | "**/Debug", 29 | "build", 30 | "**/build" 31 | ] 32 | }, 33 | "build": { 34 | "includeDir": "lwutil/src/include", 35 | "srcDir": ".", 36 | "srcFilter": "+" 37 | } 38 | } -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "default", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/build/${presetName}", 9 | "cacheVariables": { 10 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 11 | } 12 | }, 13 | { 14 | "name": "Win32-Debug", 15 | "inherits": "default", 16 | "toolchainFile": "${sourceDir}/cmake/i686-w64-mingw32-gcc.cmake", 17 | "cacheVariables": { 18 | "CMAKE_BUILD_TYPE": "Debug" 19 | } 20 | }, 21 | { 22 | "name": "Win64-Debug", 23 | "inherits": "default", 24 | "toolchainFile": "${sourceDir}/cmake/x86_64-w64-mingw32-gcc.cmake", 25 | "cacheVariables": { 26 | "CMAKE_BUILD_TYPE": "Debug" 27 | } 28 | } 29 | ], 30 | "buildPresets": [ 31 | { 32 | "name": "Win32-Debug", 33 | "configurePreset": "Win32-Debug" 34 | }, 35 | { 36 | "name": "Win64-Debug", 37 | "configurePreset": "Win64-Debug" 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /lwutil/library.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # LIB_PREFIX: LWUTIL 3 | # 4 | # This file provides set of variables for end user 5 | # and also generates one (or more) libraries, that can be added to the project using target_link_libraries(...) 6 | # 7 | # Before this file is included to the root CMakeLists file (using include() function), user can set some variables: 8 | # 9 | # LWUTIL_COMPILE_OPTIONS: If defined, it provide compiler options for generated library. 10 | # LWUTIL_COMPILE_DEFINITIONS: If defined, it provides "-D" definitions to the library build 11 | # 12 | 13 | # Custom include directory 14 | set(LWUTIL_CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib_inc) 15 | 16 | # Library core sources 17 | set(lwutil_core_SRCS 18 | ${CMAKE_CURRENT_LIST_DIR}/src/lwutil/lwutil.c 19 | ) 20 | 21 | # Setup include directories 22 | set(lwutil_include_DIRS 23 | ${CMAKE_CURRENT_LIST_DIR}/src/include 24 | ${LWUTIL_CUSTOM_INC_DIR} 25 | ) 26 | 27 | # Register library to the system 28 | add_library(lwutil) 29 | target_sources(lwutil PRIVATE ${lwutil_core_SRCS}) 30 | target_include_directories(lwutil PUBLIC ${lwutil_include_DIRS}) 31 | target_compile_options(lwutil PRIVATE ${LWUTIL_COMPILE_OPTIONS}) 32 | target_compile_definitions(lwutil PRIVATE ${LWUTIL_COMPILE_DEFINITIONS}) 33 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | # Setup project 4 | project(LwLibPROJECT) 5 | 6 | # Set default compile flags for GCC 7 | if(CMAKE_COMPILER_IS_GNUCXX) 8 | message(STATUS "GCC detected, adding compile flags") 9 | set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") 11 | endif(CMAKE_COMPILER_IS_GNUCXX) 12 | 13 | enable_testing() 14 | add_executable(${PROJECT_NAME}) 15 | target_sources(${PROJECT_NAME} PRIVATE 16 | ${CMAKE_CURRENT_LIST_DIR}/test_main.c 17 | ) 18 | target_include_directories(${PROJECT_NAME} PUBLIC 19 | ${CMAKE_CURRENT_LIST_DIR}/dev 20 | ) 21 | 22 | # Include file that can add more sources and prepare lib parameters 23 | include(${TEST_CMAKE_FILE_NAME}) 24 | 25 | # Add subdir with lwutil and link to the project 26 | add_subdirectory("../lwutil" lwutil) 27 | target_link_libraries(${PROJECT_NAME} PUBLIC lwutil) 28 | 29 | # Add additional compile settings to library, which will be propagated to executable (PUBLIC link) 30 | target_compile_options(lwutil PUBLIC -Wall -Wextra -Wpedantic) 31 | target_compile_definitions(lwutil PUBLIC WIN32 _DEBUG CONSOLE LWUTIL_DEV) 32 | 33 | # Add test 34 | add_test(NAME Test COMMAND $) -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release workflow 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 8 | 9 | jobs: 10 | # Create the release from the tag 11 | create-release: 12 | name: Create Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v2 17 | - name: Create Release 18 | id: create_release 19 | uses: actions/create-release@v1 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | with: 23 | tag_name: ${{ github.ref }} 24 | release_name: Release ${{ github.ref }} 25 | body: | 26 | See the [CHANGELOG](CHANGELOG.md) 27 | draft: false 28 | prerelease: false 29 | 30 | # Publish package to PlatformIO 31 | publish-platformio: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout code 35 | uses: actions/checkout@v4 36 | 37 | - name: Set up Python 38 | uses: actions/setup-python@v5 39 | with: 40 | python-version: "3.x" 41 | 42 | - name: Install PlatformIO 43 | run: pip install platformio 44 | 45 | - name: Publish to PlatformIO 46 | env: 47 | PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} 48 | run: pio pkg publish --type library --non-interactive 49 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import glob, os, shutil, sys, argparse 2 | 3 | def main(args): 4 | retval:int = 0 5 | 6 | rootpath = os.getcwd() 7 | files = [f for f in glob.glob(os.path.join(os.getcwd(), '**', '*.cmake'), recursive=True) if '__build__' not in f] 8 | print(files, flush=True) 9 | 10 | for file in files: 11 | basep = os.path.dirname(file) 12 | print('Test path:', basep, flush=True) 13 | 14 | # Reset directory, delete previous runs 15 | os.chdir(basep) 16 | try: shutil.rmtree('__build__/', ignore_errors=True) 17 | except: pass 18 | 19 | # Run and test 20 | os.mkdir('__build__') 21 | os.chdir('__build__') 22 | print('Configure the CMake', flush=True) 23 | retval |= os.system('cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -S../.. -G Ninja -DTEST_CMAKE_FILE_NAME={}'.format(file)) 24 | 25 | print('Compile', flush=True) 26 | retval |= os.system('cmake --build .') 27 | print('Run test', flush=True) 28 | retval |= os.system('ctest . --output-on-failure -C Debug') 29 | 30 | return retval 31 | 32 | # Get parser 33 | def get_parser(): 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument("--github", required=False, action='store_true', help="Flag if test runs on Github workflow") 36 | return parser 37 | 38 | # Run the script 39 | if __name__ == '__main__': 40 | print('Main script running', flush=True) 41 | 42 | parser = get_parser() 43 | sys.exit(1 if main(parser.parse_args()) != 0 else 0) -------------------------------------------------------------------------------- /docs/static/css/common.css: -------------------------------------------------------------------------------- 1 | /* Center aligned text */ 2 | .center { 3 | text-align: center; 4 | } 5 | 6 | /* Paragraph with main links on index page */ 7 | .index-links { 8 | text-align: center; 9 | margin-top: 10px; 10 | } 11 | .index-links a { 12 | display: inline-block; 13 | border: 1px solid #0E4263; 14 | padding: 5px 20px; 15 | margin: 2px 5px; 16 | background: #2980B9; 17 | border-radius: 4px; 18 | color: #FFFFFF; 19 | } 20 | .index-links a:hover, .index-links a:active { 21 | background: #0E4263; 22 | } 23 | 24 | /* Table header p w/0 margin */ 25 | .index-links a table thead th { 26 | vertical-align: middle; 27 | } 28 | 29 | table thead th p { 30 | margin: 0; 31 | } 32 | 33 | .table-nowrap td { 34 | white-space: normal !important; 35 | } 36 | 37 | /* Breathe output changes */ 38 | .breathe-sectiondef.container { 39 | background: #f9f9f9; 40 | padding: 10px; 41 | margin-bottom: 10px; 42 | border: 1px solid #efefef; 43 | } 44 | .breathe-sectiondef.container .breathe-sectiondef-title { 45 | background: #2980b9; 46 | color: #FFFFFF; 47 | padding: 4px; 48 | margin: -10px -10px 0 -10px; 49 | } 50 | .breathe-sectiondef.container .function, 51 | .breathe-sectiondef.container .member, 52 | .breathe-sectiondef.container .class, 53 | .breathe-sectiondef.container .type { 54 | border-bottom: 1px solid #efefef; 55 | } 56 | .breathe-sectiondef.container .function:last-child, 57 | .breathe-sectiondef.container .member:last-child, 58 | .breathe-sectiondef.container .class:last-child, 59 | .breathe-sectiondef.container .type:last-child { 60 | border-bottom: none; 61 | margin-bottom: 0; 62 | } 63 | 64 | /*# sourceMappingURL=common.css.map */ 65 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cppbuild", 6 | "label": "Build project", 7 | "command": "cmake", 8 | "args": ["--build", "${command:cmake.buildDirectory}", "-j", "8"], 9 | "options": { 10 | "cwd": "${workspaceFolder}" 11 | }, 12 | "problemMatcher": ["$gcc"], 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | } 17 | }, 18 | { 19 | "type": "shell", 20 | "label": "Re-build project", 21 | "command": "cmake", 22 | "args": ["--build", "${command:cmake.buildDirectory}", "--clean-first", "-v", "-j", "8"], 23 | "options": { 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | "problemMatcher": ["$gcc"], 27 | }, 28 | { 29 | "type": "shell", 30 | "label": "Clean project", 31 | "command": "cmake", 32 | "args": ["--build", "${command:cmake.buildDirectory}", "--target", "clean"], 33 | "options": { 34 | "cwd": "${workspaceFolder}" 35 | }, 36 | "problemMatcher": [] 37 | }, 38 | { 39 | "type": "shell", 40 | "label": "Run application", 41 | "command": "${command:cmake.launchTargetPath}", 42 | "args": [], 43 | "problemMatcher": [], 44 | }, 45 | { 46 | "label": "Docs: Install python plugins from requirements.txt file", 47 | "type": "shell", 48 | "command": "python -m pip install -r requirements.txt", 49 | "options": { 50 | "cwd": "${workspaceFolder}/docs" 51 | }, 52 | "problemMatcher": [] 53 | }, 54 | { 55 | "label": "Docs: Generate html", 56 | "type": "shell", 57 | "command": ".\\make html", 58 | "options": { 59 | "cwd": "${workspaceFolder}/docs" 60 | }, 61 | "problemMatcher": [] 62 | }, 63 | { 64 | "label": "Docs: Clean build directory", 65 | "type": "shell", 66 | "command": ".\\make clean", 67 | "options": { 68 | "cwd": "${workspaceFolder}/docs" 69 | }, 70 | "problemMatcher": [] 71 | }, 72 | ] 73 | } -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | LwUTIL |version| documentation 2 | ============================== 3 | 4 | Welcome to the documentation for version |version|. 5 | 6 | LwUTIL is lightweight watchdog library, primarily targeting operating systems, 7 | to watch multiple threads and reset system if one of them fails. 8 | 9 | .. image:: static/images/logo.svg 10 | :align: center 11 | 12 | .. rst-class:: center 13 | .. rst-class:: index_links 14 | 15 | :ref:`download_library` :ref:`getting_started` `Open Github `_ `Donate `_ 16 | 17 | Features 18 | ^^^^^^^^ 19 | 20 | * Written in C (C11) 21 | * Easy to use - very little platform dependency 22 | * Written for operating systems in mind 23 | 24 | Requirements 25 | ^^^^^^^^^^^^ 26 | 27 | * C compiler 28 | * Few *kB* of non-volatile memory 29 | 30 | Contribute 31 | ^^^^^^^^^^ 32 | 33 | Fresh contributions are always welcome. Simple instructions to proceed: 34 | 35 | #. Fork Github repository 36 | #. Respect `C style & coding rules `_ used by the library 37 | #. Create a pull request to ``develop`` branch with new features or bug fixes 38 | 39 | Alternatively you may: 40 | 41 | #. Report a bug 42 | #. Ask for a feature request 43 | 44 | License 45 | ^^^^^^^ 46 | 47 | .. literalinclude:: ../LICENSE 48 | 49 | Table of contents 50 | ^^^^^^^^^^^^^^^^^ 51 | 52 | .. toctree:: 53 | :maxdepth: 2 54 | :caption: Contents 55 | 56 | self 57 | get-started/index 58 | user-manual/index 59 | api-reference/index 60 | changelog/index 61 | authors/index 62 | 63 | .. toctree:: 64 | :maxdepth: 2 65 | :caption: Other projects 66 | :hidden: 67 | 68 | LwBTN - Button manager 69 | LwDTC - DateTimeCron 70 | LwESP - ESP-AT library 71 | LwEVT - Event manager 72 | LwGPS - GPS NMEA parser 73 | LwCELL - Cellular modem host AT library 74 | LwJSON - JSON parser 75 | LwMEM - Memory manager 76 | LwOW - OneWire with UART 77 | LwPKT - Packet protocol 78 | LwPRINTF - Printf 79 | LwRB - Ring buffer 80 | LwSHELL - Shell 81 | LwUTIL - Utility functions 82 | LwWDG - RTOS task watchdog -------------------------------------------------------------------------------- /docs/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
LwUTIL
15 |
16 |
17 |
18 | LwUTIL 19 |
20 |
21 |
22 | 23 | 24 | 25 | Text is not SVG - cannot display 26 | 27 | 28 |
-------------------------------------------------------------------------------- /docs/static/dark-light/common-dark-light.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | :root { 18 | --heading-color: red; 19 | --duration: 0.5s; 20 | --timing: ease; 21 | } 22 | 23 | *, 24 | ::before, 25 | ::after { 26 | box-sizing: border-box; 27 | } 28 | 29 | body { 30 | margin: 0; 31 | transition: 32 | color var(--duration) var(--timing), 33 | background-color var(--duration) var(--timing); 34 | font-family: sans-serif; 35 | font-size: 12pt; 36 | background-color: var(--background-color); 37 | color: var(--text-color); 38 | display: flex; 39 | justify-content: center; 40 | } 41 | 42 | main { 43 | margin: 1rem; 44 | max-width: 30rem; 45 | position: relative; 46 | } 47 | 48 | h1 { 49 | color: var(--heading-color); 50 | text-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); 51 | transition: text-shadow var(--duration) var(--timing); 52 | } 53 | 54 | img { 55 | max-width: 100%; 56 | height: auto; 57 | transition: filter var(--duration) var(--timing); 58 | } 59 | 60 | p { 61 | line-height: 1.5; 62 | word-wrap: break-word; 63 | overflow-wrap: break-word; 64 | hyphens: auto; 65 | } 66 | 67 | fieldset { 68 | border: solid 0.1rem; 69 | box-shadow: 0.1rem 0.1rem 0.1rem var(--shadow-color); 70 | transition: box-shadow var(--duration) var(--timing); 71 | } 72 | 73 | div { 74 | padding: 0.5rem; 75 | } 76 | 77 | aside { 78 | position: absolute; 79 | right: 0; 80 | padding: 0.5rem; 81 | } 82 | 83 | aside:nth-of-type(1) { 84 | top: 0; 85 | } 86 | 87 | aside:nth-of-type(2) { 88 | top: 3rem; 89 | } 90 | 91 | aside:nth-of-type(3) { 92 | top: 7rem; 93 | } 94 | 95 | aside:nth-of-type(4) { 96 | top: 12rem; 97 | } 98 | 99 | #content select, 100 | #content button, 101 | #content input[type="text"], 102 | #content input[type="search"] { 103 | width: 15rem; 104 | } 105 | 106 | dark-mode-toggle { 107 | --dark-mode-toggle-remember-icon-checked: url("checked.svg"); 108 | --dark-mode-toggle-remember-icon-unchecked: url("unchecked.svg"); 109 | --dark-mode-toggle-remember-font: 0.75rem "Helvetica"; 110 | --dark-mode-toggle-legend-font: bold 0.85rem "Helvetica"; 111 | --dark-mode-toggle-label-font: 0.85rem "Helvetica"; 112 | --dark-mode-toggle-color: var(--text-color); 113 | --dark-mode-toggle-background-color: none; 114 | 115 | margin-bottom: 1.5rem; 116 | } 117 | 118 | #dark-mode-toggle-1 { 119 | --dark-mode-toggle-dark-icon: url("sun.png"); 120 | --dark-mode-toggle-light-icon: url("moon.png"); 121 | } 122 | 123 | #dark-mode-toggle-2 { 124 | --dark-mode-toggle-dark-icon: url("sun.svg"); 125 | --dark-mode-toggle-light-icon: url("moon.svg"); 126 | --dark-mode-toggle-icon-size: 2rem; 127 | --dark-mode-toggle-icon-filter: invert(100%); 128 | } 129 | 130 | #dark-mode-toggle-3, 131 | #dark-mode-toggle-4 { 132 | --dark-mode-toggle-dark-icon: url("moon.png"); 133 | --dark-mode-toggle-light-icon: url("sun.png"); 134 | } 135 | 136 | #dark-mode-toggle-3 { 137 | --dark-mode-toggle-remember-filter: invert(100%); 138 | } 139 | 140 | #dark-mode-toggle-4 { 141 | --dark-mode-toggle-active-mode-background-color: var(--accent-color); 142 | --dark-mode-toggle-remember-filter: invert(100%); 143 | } 144 | -------------------------------------------------------------------------------- /docs/get-started/index.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | Getting started 4 | =============== 5 | 6 | Getting started may be the most challenging part of every new library. 7 | This guide is describing how to start with the library quickly and effectively 8 | 9 | .. _download_library: 10 | 11 | Download library 12 | ^^^^^^^^^^^^^^^^ 13 | 14 | Library is primarily hosted on `Github `_. 15 | 16 | You can get it by: 17 | 18 | * Downloading latest release from `releases area `_ on Github 19 | * Cloning ``main`` branch for latest stable version 20 | * Cloning ``develop`` branch for latest development 21 | 22 | Download from releases 23 | ********************** 24 | 25 | All releases are available on Github `releases area `_. 26 | 27 | Clone from Github 28 | ***************** 29 | 30 | First-time clone 31 | """""""""""""""" 32 | 33 | This is used when you do not have yet local copy on your machine. 34 | 35 | * Make sure ``git`` is installed. 36 | * Open console and navigate to path in the system to clone repository to. Use command ``cd your_path`` 37 | * Clone repository with one of available options below 38 | 39 | * Run ``git clone --recurse-submodules https://github.com/MaJerle/lwutil`` command to clone entire repository, including submodules 40 | * Run ``git clone --recurse-submodules --branch develop https://github.com/MaJerle/lwutil`` to clone `development` branch, including submodules 41 | * Run ``git clone --recurse-submodules --branch main https://github.com/MaJerle/lwutil`` to clone `latest stable` branch, including submodules 42 | 43 | * Navigate to ``examples`` directory and run favourite example 44 | 45 | Update cloned to latest version 46 | """"""""""""""""""""""""""""""" 47 | 48 | * Open console and navigate to path in the system where your repository is located. Use command ``cd your_path`` 49 | * Run ``git pull origin main`` command to get latest changes on ``main`` branch 50 | * Run ``git pull origin develop`` command to get latest changes on ``develop`` branch 51 | * Run ``git submodule update --init --remote`` to update submodules to latest version 52 | 53 | .. note:: 54 | This is preferred option to use when you want to evaluate library and run prepared examples. 55 | Repository consists of multiple submodules which can be automatically downloaded when cloning and pulling changes from root repository. 56 | 57 | Add library to project 58 | ^^^^^^^^^^^^^^^^^^^^^^ 59 | 60 | At this point it is assumed that you have successfully download library, either with ``git clone`` command or with manual download from the library releases page. 61 | Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path. 62 | 63 | *CMake* is the main supported build system. Package comes with the ``CMakeLists.txt`` and ``library.cmake`` files, both located in the ``lwutil`` directory: 64 | 65 | * ``library.cmake``: It is a fully configured set of variables and with library definition. User can include this file to the project file with ``include(path/to/library.cmake)`` and then manually use the variables provided by the file, such as list of source files, include paths or necessary compiler definitions. It is up to the user to properly use the this file on its own. 66 | * ``CMakeLists.txt``: It is a wrapper-only file and includes ``library.cmake`` file. It is used for when user wants to include the library to the main project by simply calling *CMake* ``add_subdirectory`` command, followed by ``target_link_libraries`` to link external library to the final project. 67 | 68 | .. tip:: 69 | Open ``library.cmake`` and analyze the provided information. Among variables, you can also find list of all possible exposed libraries for the user. 70 | 71 | If you do not use the *CMake*, you can do the following: 72 | 73 | * Copy ``lwutil`` folder to your project, it contains library files 74 | * Add ``lwutil/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag 75 | * Add source files from ``lwutil/src/`` folder to toolchain build. These files are built by `C/C++` compiler 76 | * Build the project 77 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | from sphinx.builders.html import StandaloneHTMLBuilder 17 | import subprocess, os 18 | 19 | # Run doxygen first 20 | # read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' 21 | # if read_the_docs_build: 22 | subprocess.call('doxygen doxyfile.doxy', shell=True) 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = 'LwUTIL' 26 | copyright = '2025, Tilen MAJERLE' 27 | author = 'Tilen MAJERLE' 28 | 29 | # Try to get branch at which this is running 30 | # and try to determine which version to display in sphinx 31 | # Version is using git tag if on master/main or "latest-develop" if on develop branch 32 | version = '' 33 | git_branch = '' 34 | 35 | def cmd_exec_print(t): 36 | print("cmd > ", t, "\n", os.popen(t).read().strip(), "\n") 37 | 38 | # Print demo data here 39 | cmd_exec_print('git branch') 40 | cmd_exec_print('git describe') 41 | cmd_exec_print('git describe --tags') 42 | cmd_exec_print('git describe --tags --abbrev=0') 43 | cmd_exec_print('git describe --tags --abbrev=1') 44 | 45 | # Get current branch 46 | res = os.popen('git branch').read().strip() 47 | for line in res.split("\n"): 48 | if line[0] == '*': 49 | git_branch = line[1:].strip() 50 | 51 | # Decision for display version 52 | git_branch = git_branch.replace('(HEAD detached at ', '').replace(')', '') 53 | if git_branch.find('master') >= 0 or git_branch.find('main') >= 0: 54 | #version = os.popen('git describe --tags --abbrev=0').read().strip() 55 | version = 'latest-stable' 56 | elif git_branch.find('develop-') >= 0 or git_branch.find('develop/') >= 0: 57 | version = 'branch-' + git_branch 58 | elif git_branch == 'develop' or git_branch == 'origin/develop': 59 | version = 'latest-develop' 60 | else: 61 | version = os.popen('git describe --tags --abbrev=0').read().strip() 62 | 63 | # For debugging purpose only 64 | print("GIT BRANCH: " + git_branch) 65 | print("PROJ VERSION: " + version) 66 | 67 | # -- General configuration --------------------------------------------------- 68 | 69 | # Add any Sphinx extension module names here, as strings. They can be 70 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 71 | # ones. 72 | extensions = [ 73 | 'sphinx.ext.autodoc', 74 | 'sphinx.ext.intersphinx', 75 | 'sphinx.ext.autosectionlabel', 76 | 'sphinx.ext.todo', 77 | 'sphinx.ext.coverage', 78 | 'sphinx.ext.mathjax', 79 | 'sphinx.ext.ifconfig', 80 | 'sphinx.ext.viewcode', 81 | 'sphinx_sitemap', 82 | 83 | 'breathe', 84 | ] 85 | 86 | # Add any paths that contain templates here, relative to this directory. 87 | templates_path = ['templates'] 88 | 89 | # List of patterns, relative to source directory, that match files and 90 | # directories to ignore when looking for source files. 91 | # This pattern also affects html_static_path and html_extra_path. 92 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 93 | 94 | highlight_language = 'c' 95 | 96 | # -- Options for HTML output ------------------------------------------------- 97 | 98 | # The theme to use for HTML and HTML Help pages. See the documentation for 99 | # a list of builtin themes. 100 | # 101 | html_theme = 'sphinx_rtd_theme' 102 | html_theme_options = { 103 | 'canonical_url': '', 104 | 'analytics_id': '', # Provided by Google in your dashboard 105 | 'display_version': True, 106 | 'prev_next_buttons_location': 'bottom', 107 | 'style_external_links': False, 108 | 109 | 'logo_only': False, 110 | 111 | # Toc options 112 | 'collapse_navigation': True, 113 | 'sticky_navigation': True, 114 | 'navigation_depth': 4, 115 | 'includehidden': True, 116 | 'titles_only': False 117 | } 118 | html_logo = 'static/images/logo.svg' 119 | github_url = 'https://github.com/MaJerle/lwutil' 120 | html_baseurl = 'https://docs.majerle.eu/projects/lwutil/' 121 | 122 | # Add any paths that contain custom static files (such as style sheets) here, 123 | # relative to this directory. They are copied after the builtin static files, 124 | # so a file named "default.css" will overwrite the builtin "default.css". 125 | html_static_path = ['static'] 126 | html_css_files = [ 127 | 'css/common.css', 128 | 'css/custom.css', 129 | 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css', 130 | ] 131 | html_js_files = [ 132 | '' 133 | ] 134 | 135 | # Master index file 136 | master_doc = 'index' 137 | 138 | # --- Breathe configuration ----------------------------------------------------- 139 | breathe_projects = { 140 | "lwutil": "_build/xml/" 141 | } 142 | breathe_default_project = "lwutil" 143 | breathe_default_members = ('members', 'undoc-members') 144 | breathe_show_enumvalue_initializer = True -------------------------------------------------------------------------------- /lwutil/src/lwutil/lwutil.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file lwutil.c 3 | * \brief Lightweight utility library 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2024 Tilen MAJERLE 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without restriction, 12 | * including without limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of the Software, 14 | * and to permit persons to whom the Software is furnished to do so, 15 | * subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * This file is part of LwUTIL - Lightweight utility library. 30 | * 31 | * Author: Tilen MAJERLE 32 | * Version: v1.4.0 33 | */ 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "lwutil/lwutil.h" 39 | 40 | /** 41 | * \brief Makes ascii char array from `unsigned 8-bit` value 42 | * \param[in] hex: Hexadecimal data to be converted 43 | * \param[out] ascii: Minimum `3-bytes` long array to write value to 44 | */ 45 | void 46 | lwutil_u8_to_2asciis(uint8_t hex, char* ascii) { 47 | for (uint8_t idx = 2U; idx != 0; --idx) { 48 | uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU; 49 | 50 | aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U); 51 | ascii[2U - idx] = (char)aux; 52 | } 53 | ascii[2U] = '\0'; 54 | } 55 | 56 | /** 57 | * \brief Makes ascii char array from `unsigned 16-bit` value 58 | * \param[in] hex: Hexadecimal data to be converted 59 | * \param[out] ascii: Minimum `5-bytes` long array to write value to 60 | */ 61 | void 62 | lwutil_u16_to_4asciis(uint16_t hex, char* ascii) { 63 | for (uint8_t idx = 4U; idx != 0; --idx) { 64 | uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU; 65 | 66 | aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U); 67 | ascii[4U - idx] = (char)aux; 68 | } 69 | ascii[4] = '\0'; 70 | } 71 | 72 | /** 73 | * \brief Makes ascii char array from `unsigned 32-bit` value 74 | * \param[in] hex: Hexadecimal data to be converted 75 | * \param[out] ascii: Minimum `9-bytes` long array to write value to 76 | */ 77 | void 78 | lwutil_u32_to_8asciis(uint32_t hex, char* ascii) { 79 | for (uint8_t idx = 8U; idx != 0; --idx) { 80 | uint8_t aux = (uint8_t)(hex >> (4U * (idx - 1U))) & 0x0FU; 81 | 82 | aux = (aux <= 9U) ? (aux + 0x30U) : (aux + 0x57U); 83 | ascii[8U - idx] = (char)aux; 84 | } 85 | ascii[8] = '\0'; 86 | } 87 | 88 | /** 89 | * \brief Load variable length integer from the byte stream to the variable. 90 | * 91 | * Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80), 92 | * which tells us if there is a next byte to be used as part of the number sequence. 93 | * 94 | * 32-bit integer can be stored with anything between `1` and `5` bytes. 95 | * 96 | * \param ptr: Array pointer to load data from 97 | * \param ptr_len: Input array length 98 | * \param val_out: Pointer to variable to write result value 99 | * \return Number of bytes used to load the full number. Set to `0` in case of an error. 100 | */ 101 | uint8_t 102 | lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out) { 103 | size_t cnt = 0; 104 | uint32_t val = 0; 105 | const uint8_t* p_data = ptr; 106 | uint8_t byt; 107 | 108 | if (ptr == NULL || ptr_len == 0 || val_out == NULL) { 109 | return 0; 110 | } 111 | do { 112 | byt = *p_data++; 113 | val |= ((uint32_t)(byt & 0x7FU)) << (cnt * 7U); 114 | ++cnt; 115 | } while (--ptr_len > 0 && (byt & 0x80U) > 0); 116 | 117 | /* Check memory length */ 118 | if ((byt & 0x80U) > 0) { 119 | val = 0; 120 | cnt = 0; 121 | } 122 | *val_out = val; 123 | return cnt; 124 | } 125 | 126 | /** 127 | * \brief Store an integer into variable length byte sequence array. 128 | * 129 | * Variable length integers (short varints) are stored with continuation bit (bit 7, 0x80), 130 | * which tells us if there is a next byte to be used as part of the number sequence. 131 | * 132 | * 32-bit integer can be stored with anything between `1` and `5` bytes. 133 | * 134 | * \param val: Value to encode into byte sequence 135 | * \param ptr: Array to write output result 136 | * \param ptr_len: Length of an input array 137 | * \return Number of bytes written (stored) in the memory. Set to `0` in case of an error. 138 | */ 139 | uint8_t 140 | lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len) { 141 | uint8_t* p_data = ptr; 142 | size_t cnt = 0; 143 | 144 | if (ptr == NULL || ptr_len == 0) { 145 | return 0; 146 | } 147 | do { 148 | *p_data++ = (val & 0x7FU) | (val > 0x7FU ? 0x80U : 0x00U); 149 | val >>= 7U; 150 | ++cnt; 151 | } while (--ptr_len > 0 && val > 0); 152 | 153 | /* Memory check */ 154 | if (val > 0) { 155 | cnt = 0; 156 | } 157 | return cnt; 158 | } 159 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Language part removed. With clang-format >=20.1, the C and Cpp are separately handled, 2 | # so either there is no language at all, or we need to create 2 formats for C and Cpp, separately 3 | 4 | --- 5 | # Language: Cpp 6 | # BasedOnStyle: LLVM 7 | AccessModifierOffset: -2 8 | AlignAfterOpenBracket: Align 9 | AlignArrayOfStructures: None 10 | AlignConsecutiveMacros: 11 | Enabled: true 12 | AcrossEmptyLines: true 13 | AcrossComments: true 14 | AlignConsecutiveAssignments: None 15 | AlignConsecutiveBitFields: 16 | Enabled: true 17 | AcrossEmptyLines: true 18 | AcrossComments: true 19 | AlignConsecutiveDeclarations: None 20 | AlignEscapedNewlines: Right 21 | AlignOperands: Align 22 | SortIncludes: true 23 | InsertBraces: true # Control statements must have curly brackets 24 | AlignTrailingComments: true 25 | AllowAllArgumentsOnNextLine: true 26 | AllowAllParametersOfDeclarationOnNextLine: true 27 | AllowShortEnumsOnASingleLine: true 28 | AllowShortBlocksOnASingleLine: Empty 29 | AllowShortCaseLabelsOnASingleLine: true 30 | AllowShortFunctionsOnASingleLine: All 31 | AllowShortLambdasOnASingleLine: All 32 | AllowShortIfStatementsOnASingleLine: Never 33 | AllowShortLoopsOnASingleLine: false 34 | AlwaysBreakAfterDefinitionReturnType: None 35 | AlwaysBreakAfterReturnType: AllDefinitions 36 | AlwaysBreakBeforeMultilineStrings: false 37 | AlwaysBreakTemplateDeclarations: Yes 38 | AttributeMacros: 39 | - __capability 40 | BinPackArguments: true 41 | BinPackParameters: true 42 | BraceWrapping: 43 | AfterCaseLabel: false 44 | AfterClass: false 45 | AfterControlStatement: Never 46 | AfterEnum: false 47 | AfterFunction: false 48 | AfterNamespace: false 49 | AfterObjCDeclaration: false 50 | AfterStruct: false 51 | AfterUnion: false 52 | AfterExternBlock: false 53 | BeforeCatch: false 54 | BeforeElse: false 55 | BeforeLambdaBody: false 56 | BeforeWhile: false 57 | IndentBraces: false 58 | SplitEmptyFunction: true 59 | SplitEmptyRecord: true 60 | SplitEmptyNamespace: true 61 | BreakBeforeBinaryOperators: NonAssignment 62 | BreakBeforeConceptDeclarations: true 63 | BreakBeforeBraces: Attach 64 | BreakBeforeInheritanceComma: false 65 | BreakInheritanceList: BeforeColon 66 | BreakBeforeTernaryOperators: true 67 | BreakConstructorInitializersBeforeComma: false 68 | BreakConstructorInitializers: BeforeColon 69 | BreakAfterJavaFieldAnnotations: false 70 | BreakStringLiterals: true 71 | ColumnLimit: 120 72 | CommentPragmas: "^ IWYU pragma:" 73 | QualifierAlignment: Leave 74 | CompactNamespaces: false 75 | ConstructorInitializerIndentWidth: 4 76 | ContinuationIndentWidth: 4 77 | Cpp11BracedListStyle: true 78 | DeriveLineEnding: true 79 | DerivePointerAlignment: false 80 | DisableFormat: false 81 | EmptyLineAfterAccessModifier: Never 82 | EmptyLineBeforeAccessModifier: LogicalBlock 83 | ExperimentalAutoDetectBinPacking: false 84 | PackConstructorInitializers: BinPack 85 | BasedOnStyle: "" 86 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 87 | AllowAllConstructorInitializersOnNextLine: true 88 | FixNamespaceComments: true 89 | ForEachMacros: 90 | - foreach 91 | - Q_FOREACH 92 | - BOOST_FOREACH 93 | IfMacros: 94 | - KJ_IF_MAYBE 95 | IncludeBlocks: Preserve 96 | IncludeCategories: 97 | - Regex: "^<(.*)>" 98 | Priority: 0 99 | - Regex: '^"(.*)"' 100 | Priority: 1 101 | - Regex: "(.*)" 102 | Priority: 2 103 | IncludeIsMainRegex: "(Test)?$" 104 | IncludeIsMainSourceRegex: "" 105 | IndentAccessModifiers: false 106 | IndentCaseLabels: true 107 | IndentCaseBlocks: false 108 | IndentGotoLabels: true 109 | IndentPPDirectives: None 110 | IndentExternBlock: AfterExternBlock 111 | IndentRequires: true 112 | IndentWidth: 4 113 | IndentWrappedFunctionNames: false 114 | InsertTrailingCommas: None 115 | JavaScriptQuotes: Leave 116 | JavaScriptWrapImports: true 117 | KeepEmptyLinesAtTheStartOfBlocks: true 118 | LambdaBodyIndentation: Signature 119 | MacroBlockBegin: "" 120 | MacroBlockEnd: "" 121 | MaxEmptyLinesToKeep: 1 122 | NamespaceIndentation: None 123 | ObjCBinPackProtocolList: Auto 124 | ObjCBlockIndentWidth: 2 125 | ObjCBreakBeforeNestedBlockParam: true 126 | ObjCSpaceAfterProperty: false 127 | ObjCSpaceBeforeProtocolList: true 128 | PenaltyBreakAssignment: 2 129 | PenaltyBreakBeforeFirstCallParameter: 19 130 | PenaltyBreakComment: 300 131 | PenaltyBreakFirstLessLess: 120 132 | PenaltyBreakOpenParenthesis: 0 133 | PenaltyBreakString: 1000 134 | PenaltyBreakTemplateDeclaration: 10 135 | PenaltyExcessCharacter: 1000000 136 | PenaltyReturnTypeOnItsOwnLine: 60 137 | PenaltyIndentedWhitespace: 0 138 | PointerAlignment: Left 139 | PPIndentWidth: -1 140 | ReferenceAlignment: Pointer 141 | ReflowComments: false 142 | RemoveBracesLLVM: false 143 | SeparateDefinitionBlocks: Always 144 | ShortNamespaceLines: 1 145 | SortJavaStaticImport: Before 146 | SortUsingDeclarations: true 147 | SpaceAfterCStyleCast: false 148 | SpaceAfterLogicalNot: false 149 | SpaceAfterTemplateKeyword: true 150 | SpaceBeforeAssignmentOperators: true 151 | SpaceBeforeCaseColon: false 152 | SpaceBeforeParens: ControlStatements 153 | SpaceBeforeParensOptions: 154 | AfterControlStatements: true 155 | AfterForeachMacros: true 156 | AfterFunctionDefinitionName: false 157 | AfterFunctionDeclarationName: false 158 | AfterIfMacros: true 159 | AfterOverloadedOperator: false 160 | BeforeNonEmptyParentheses: false 161 | SpaceAroundPointerQualifiers: Default 162 | SpaceBeforeRangeBasedForLoopColon: true 163 | SpaceInEmptyBlock: false 164 | SpaceInEmptyParentheses: false 165 | SpacesBeforeTrailingComments: 1 166 | SpacesInAngles: Never 167 | SpacesInConditionalStatement: false 168 | SpacesInContainerLiterals: true 169 | SpacesInCStyleCastParentheses: false 170 | SpacesInLineCommentPrefix: 171 | Minimum: 1 172 | Maximum: -1 173 | SpacesInParentheses: false 174 | SpacesInSquareBrackets: false 175 | SpaceBeforeSquareBrackets: false 176 | BitFieldColonSpacing: Both 177 | Standard: Latest 178 | StatementAttributeLikeMacros: 179 | - Q_EMIT 180 | StatementMacros: 181 | - Q_UNUSED 182 | - QT_REQUIRE_VERSION 183 | TabWidth: 8 184 | UseCRLF: false 185 | UseTab: Never 186 | WhitespaceSensitiveMacros: 187 | - STRINGIZE 188 | - PP_STRINGIZE 189 | - BOOST_PP_STRINGIZE 190 | - NS_SWIFT_NAME 191 | - CF_SWIFT_NAME 192 | SpaceBeforeCpp11BracedList: false 193 | SpaceBeforeCtorInitializerColon: true 194 | SpaceBeforeInheritanceColon: true 195 | --- 196 | 197 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Build Keil files 2 | *.rar 3 | *.o 4 | *.d 5 | *.crf 6 | *.htm 7 | *.dep 8 | *.map 9 | *.bak 10 | *.axf 11 | *.lnp 12 | *.lst 13 | *.ini 14 | *.scvd 15 | *.iex 16 | *.sct 17 | *.MajerleT 18 | *.tjuln 19 | *.tilen 20 | *.dbgconf 21 | *.uvguix 22 | *.uvoptx 23 | *.__i 24 | *.i 25 | *.txt 26 | !docs/*.txt 27 | !CMakeLists.txt 28 | RTE/ 29 | 30 | *debug 31 | 32 | # IAR Settings 33 | **/settings/*.crun 34 | **/settings/*.dbgdt 35 | **/settings/*.cspy 36 | **/settings/*.cspy.* 37 | **/settings/*.xcl 38 | **/settings/*.dni 39 | **/settings/*.wsdt 40 | **/settings/*.wspos 41 | 42 | # IAR Debug Exe 43 | **/Exe/*.sim 44 | 45 | # IAR Debug Obj 46 | **/Obj/*.pbd 47 | **/Obj/*.pbd.* 48 | **/Obj/*.pbi 49 | **/Obj/*.pbi.* 50 | 51 | *.TMP 52 | /docs_src/x_Doxyfile.doxy 53 | 54 | .DS_Store 55 | 56 | ## Ignore Visual Studio temporary files, build results, and 57 | ## files generated by popular Visual Studio add-ons. 58 | ## 59 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 60 | 61 | # User-specific files 62 | *.suo 63 | *.user 64 | *.userosscache 65 | *.sln.docstates 66 | 67 | # User-specific files (MonoDevelop/Xamarin Studio) 68 | *.userprefs 69 | 70 | # Build results 71 | [Dd]ebug/ 72 | [Dd]ebugPublic/ 73 | [Rr]elease/ 74 | [Rr]eleases/ 75 | [Dd]ebug*/ 76 | x64/ 77 | x86/ 78 | bld/ 79 | [Bb]in/ 80 | [Oo]bj/ 81 | [Ll]og/ 82 | _build/ 83 | build/ 84 | __build__/ 85 | 86 | # Visual Studio 2015/2017 cache/options directory 87 | .vs/ 88 | # Uncomment if you have tasks that create the project's static files in wwwroot 89 | #wwwroot/ 90 | 91 | # Visual Studio 2017 auto generated files 92 | Generated\ Files/ 93 | 94 | # MSTest test Results 95 | [Tt]est[Rr]esult*/ 96 | [Bb]uild[Ll]og.* 97 | 98 | # NUNIT 99 | *.VisualState.xml 100 | TestResult.xml 101 | 102 | # Build Results of an ATL Project 103 | [Dd]ebugPS/ 104 | [Rr]eleasePS/ 105 | dlldata.c 106 | 107 | # Benchmark Results 108 | BenchmarkDotNet.Artifacts/ 109 | 110 | # .NET Core 111 | project.lock.json 112 | project.fragment.lock.json 113 | artifacts/ 114 | **/Properties/launchSettings.json 115 | 116 | # StyleCop 117 | StyleCopReport.xml 118 | 119 | # Files built by Visual Studio 120 | *_i.c 121 | *_p.c 122 | *_i.h 123 | *.ilk 124 | *.meta 125 | *.obj 126 | *.pch 127 | *.pdb 128 | *.pgc 129 | *.pgd 130 | *.rsp 131 | *.sbr 132 | *.tlb 133 | *.tli 134 | *.tlh 135 | *.tmp 136 | *.tmp_proj 137 | *.log 138 | *.vspscc 139 | *.vssscc 140 | .builds 141 | *.pidb 142 | *.svclog 143 | *.scc 144 | *.out 145 | *.sim 146 | 147 | # Chutzpah Test files 148 | _Chutzpah* 149 | 150 | # Visual C++ cache files 151 | ipch/ 152 | *.aps 153 | *.ncb 154 | *.opendb 155 | *.opensdf 156 | *.sdf 157 | *.cachefile 158 | *.VC.db 159 | *.VC.VC.opendb 160 | 161 | # Visual Studio profiler 162 | *.psess 163 | *.vsp 164 | *.vspx 165 | *.sap 166 | 167 | # Visual Studio Trace Files 168 | *.e2e 169 | 170 | # TFS 2012 Local Workspace 171 | $tf/ 172 | 173 | # Guidance Automation Toolkit 174 | *.gpState 175 | 176 | # ReSharper is a .NET coding add-in 177 | _ReSharper*/ 178 | *.[Rr]e[Ss]harper 179 | *.DotSettings.user 180 | 181 | # JustCode is a .NET coding add-in 182 | .JustCode 183 | 184 | # TeamCity is a build add-in 185 | _TeamCity* 186 | 187 | # DotCover is a Code Coverage Tool 188 | *.dotCover 189 | 190 | # AxoCover is a Code Coverage Tool 191 | .axoCover/* 192 | !.axoCover/settings.json 193 | 194 | # Visual Studio code coverage results 195 | *.coverage 196 | *.coveragexml 197 | 198 | # NCrunch 199 | _NCrunch_* 200 | .*crunch*.local.xml 201 | nCrunchTemp_* 202 | 203 | # MightyMoose 204 | *.mm.* 205 | AutoTest.Net/ 206 | 207 | # Web workbench (sass) 208 | .sass-cache/ 209 | 210 | # Installshield output folder 211 | [Ee]xpress/ 212 | 213 | # DocProject is a documentation generator add-in 214 | DocProject/buildhelp/ 215 | DocProject/Help/*.HxT 216 | DocProject/Help/*.HxC 217 | DocProject/Help/*.hhc 218 | DocProject/Help/*.hhk 219 | DocProject/Help/*.hhp 220 | DocProject/Help/Html2 221 | DocProject/Help/html 222 | 223 | # Click-Once directory 224 | publish/ 225 | 226 | # Publish Web Output 227 | *.[Pp]ublish.xml 228 | *.azurePubxml 229 | # Note: Comment the next line if you want to checkin your web deploy settings, 230 | # but database connection strings (with potential passwords) will be unencrypted 231 | *.pubxml 232 | *.publishproj 233 | 234 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 235 | # checkin your Azure Web App publish settings, but sensitive information contained 236 | # in these scripts will be unencrypted 237 | PublishScripts/ 238 | 239 | # NuGet Packages 240 | *.nupkg 241 | # The packages folder can be ignored because of Package Restore 242 | **/[Pp]ackages/* 243 | # except build/, which is used as an MSBuild target. 244 | !**/[Pp]ackages/build/ 245 | # Uncomment if necessary however generally it will be regenerated when needed 246 | #!**/[Pp]ackages/repositories.config 247 | # NuGet v3's project.json files produces more ignorable files 248 | *.nuget.props 249 | *.nuget.targets 250 | 251 | # Microsoft Azure Build Output 252 | csx/ 253 | *.build.csdef 254 | 255 | # Microsoft Azure Emulator 256 | ecf/ 257 | rcf/ 258 | 259 | # Windows Store app package directories and files 260 | AppPackages/ 261 | BundleArtifacts/ 262 | Package.StoreAssociation.xml 263 | _pkginfo.txt 264 | *.appx 265 | 266 | # Visual Studio cache files 267 | # files ending in .cache can be ignored 268 | *.[Cc]ache 269 | # but keep track of directories ending in .cache 270 | !*.[Cc]ache/ 271 | 272 | # Others 273 | ClientBin/ 274 | ~$* 275 | *~ 276 | *.dbmdl 277 | *.dbproj.schemaview 278 | *.jfm 279 | *.pfx 280 | *.publishsettings 281 | orleans.codegen.cs 282 | 283 | # Including strong name files can present a security risk 284 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 285 | #*.snk 286 | 287 | # Since there are multiple workflows, uncomment next line to ignore bower_components 288 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 289 | #bower_components/ 290 | 291 | # RIA/Silverlight projects 292 | Generated_Code/ 293 | 294 | # Backup & report files from converting an old project file 295 | # to a newer Visual Studio version. Backup files are not needed, 296 | # because we have git ;-) 297 | _UpgradeReport_Files/ 298 | Backup*/ 299 | UpgradeLog*.XML 300 | UpgradeLog*.htm 301 | 302 | # SQL Server files 303 | *.mdf 304 | *.ldf 305 | *.ndf 306 | 307 | # Business Intelligence projects 308 | *.rdl.data 309 | *.bim.layout 310 | *.bim_*.settings 311 | 312 | # Microsoft Fakes 313 | FakesAssemblies/ 314 | 315 | # GhostDoc plugin setting file 316 | *.GhostDoc.xml 317 | 318 | # Node.js Tools for Visual Studio 319 | .ntvs_analysis.dat 320 | node_modules/ 321 | 322 | # TypeScript v1 declaration files 323 | typings/ 324 | 325 | # Visual Studio 6 build log 326 | *.plg 327 | 328 | # Visual Studio 6 workspace options file 329 | *.opt 330 | 331 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 332 | *.vbw 333 | 334 | # Visual Studio LightSwitch build output 335 | **/*.HTMLClient/GeneratedArtifacts 336 | **/*.DesktopClient/GeneratedArtifacts 337 | **/*.DesktopClient/ModelManifest.xml 338 | **/*.Server/GeneratedArtifacts 339 | **/*.Server/ModelManifest.xml 340 | _Pvt_Extensions 341 | 342 | # Paket dependency manager 343 | .paket/paket.exe 344 | paket-files/ 345 | 346 | # FAKE - F# Make 347 | .fake/ 348 | 349 | # JetBrains Rider 350 | .idea/ 351 | *.sln.iml 352 | 353 | # CodeRush 354 | .cr/ 355 | 356 | # Python Tools for Visual Studio (PTVS) 357 | __pycache__/ 358 | *.pyc 359 | 360 | # Cake - Uncomment if you are using it 361 | # tools/** 362 | # !tools/packages.config 363 | 364 | # Tabs Studio 365 | *.tss 366 | 367 | # Telerik's JustMock configuration file 368 | *.jmconfig 369 | 370 | # BizTalk build output 371 | *.btp.cs 372 | *.btm.cs 373 | *.odx.cs 374 | *.xsd.cs 375 | 376 | # OpenCover UI analysis results 377 | OpenCover/ 378 | 379 | # Azure Stream Analytics local run output 380 | ASALocalRun/ 381 | 382 | # MSBuild Binary and Structured Log 383 | *.binlog 384 | 385 | log_file.txt 386 | .metadata/ 387 | .mxproject 388 | .settings/ 389 | project.ioc 390 | mx.scratch 391 | *.tilen majerle 392 | 393 | 394 | # Altium 395 | Project outputs* 396 | History/ 397 | *.SchDocPreview 398 | *.$$$Preview 399 | 400 | # VSCode projects 401 | project_vscode_compiled.exe -------------------------------------------------------------------------------- /tests/test_full/test_full.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lwutil/lwutil.h" 4 | 5 | #define TEST_IF_TRUE(condition) \ 6 | if (!(condition)) { \ 7 | printf("Condition %s failed on line %d\r\n", #condition, (int)__LINE__); \ 8 | retval = -1; \ 9 | } 10 | 11 | int 12 | test_run(void) { 13 | int retval = 0; 14 | 15 | /* Test storing integer device */ 16 | { 17 | uint8_t arr[10U]; 18 | uint8_t* ptr; 19 | 20 | /* Standard version */ 21 | memset(arr, 0x00, sizeof(arr)); 22 | lwutil_st_u16_le(0x1234U, arr); 23 | TEST_IF_TRUE(arr[0] == 0x34U); 24 | TEST_IF_TRUE(arr[1] == 0x12U); 25 | TEST_IF_TRUE(arr[2] == 0x00U); 26 | 27 | memset(arr, 0x00, sizeof(arr)); 28 | lwutil_st_u16_be(0x1234, arr); 29 | TEST_IF_TRUE(arr[0] == 0x12U); 30 | TEST_IF_TRUE(arr[1] == 0x34U); 31 | TEST_IF_TRUE(arr[2] == 0x00U); 32 | 33 | memset(arr, 0x00, sizeof(arr)); 34 | lwutil_st_u32_le(0x12345678U, arr); 35 | TEST_IF_TRUE(arr[0] == 0x78U); 36 | TEST_IF_TRUE(arr[1] == 0x56U); 37 | TEST_IF_TRUE(arr[2] == 0x34U); 38 | TEST_IF_TRUE(arr[3] == 0x12U); 39 | TEST_IF_TRUE(arr[4] == 0x00U); 40 | 41 | memset(arr, 0x00, sizeof(arr)); 42 | lwutil_st_u32_be(0x12345678, arr); 43 | TEST_IF_TRUE(arr[0] == 0x12U); 44 | TEST_IF_TRUE(arr[1] == 0x34U); 45 | TEST_IF_TRUE(arr[2] == 0x56U); 46 | TEST_IF_TRUE(arr[3] == 0x78U); 47 | TEST_IF_TRUE(arr[4] == 0x00U); 48 | 49 | /* 50 | * Extended version accepts the pointer to pointer to the memory, 51 | * reads the data from the pointer, and then changes the address pointer points too. 52 | * 53 | * This allows the following "load" code: 54 | * 55 | * uint8_t data[10]; 56 | * uint8_t* data_ptr = data; 57 | * 58 | * //Write entries one after another in the data array 59 | * lwutil_st_u16_le_ex(0x1234, (void**)&data_ptr); 60 | * lwutil_st_u16_le_ex(0x5678, (void**)&data_ptr); 61 | * 62 | * the final data array will look like: 0x34, 0x12, 0x78, 0x56 63 | */ 64 | memset(arr, 0x00, sizeof(arr)); 65 | ptr = arr; 66 | lwutil_st_u16_le_ex(0x1234U, (void**)&ptr); 67 | lwutil_st_u16_le_ex(0x5678U, (void**)&ptr); 68 | TEST_IF_TRUE(arr[0] == 0x34U); 69 | TEST_IF_TRUE(arr[1] == 0x12U); 70 | TEST_IF_TRUE(arr[2] == 0x78U); 71 | TEST_IF_TRUE(arr[3] == 0x56U); 72 | TEST_IF_TRUE(arr[4] == 0x00U); 73 | TEST_IF_TRUE(ptr == &arr[4]); 74 | 75 | memset(arr, 0x00, sizeof(arr)); 76 | ptr = arr; 77 | lwutil_st_u16_be_ex(0x1234U, (void**)&ptr); 78 | lwutil_st_u16_be_ex(0x5678U, (void**)&ptr); 79 | TEST_IF_TRUE(arr[0] == 0x12U); 80 | TEST_IF_TRUE(arr[1] == 0x34U); 81 | TEST_IF_TRUE(arr[2] == 0x56U); 82 | TEST_IF_TRUE(arr[3] == 0x78U); 83 | TEST_IF_TRUE(arr[4] == 0x00U); 84 | TEST_IF_TRUE(ptr == &arr[4]); 85 | 86 | memset(arr, 0x00, sizeof(arr)); 87 | ptr = arr; 88 | lwutil_st_u32_le_ex(0x12345678U, (void**)&ptr); 89 | lwutil_st_u32_le_ex(0x12345678U, (void**)&ptr); 90 | TEST_IF_TRUE(arr[0] == 0x78U); 91 | TEST_IF_TRUE(arr[1] == 0x56U); 92 | TEST_IF_TRUE(arr[2] == 0x34U); 93 | TEST_IF_TRUE(arr[3] == 0x12U); 94 | TEST_IF_TRUE(arr[4] == 0x78U); 95 | TEST_IF_TRUE(arr[5] == 0x56U); 96 | TEST_IF_TRUE(arr[6] == 0x34U); 97 | TEST_IF_TRUE(arr[7] == 0x12U); 98 | TEST_IF_TRUE(arr[8] == 0x00U); 99 | TEST_IF_TRUE(ptr == &arr[8]); 100 | 101 | memset(arr, 0x00, sizeof(arr)); 102 | ptr = arr; 103 | lwutil_st_u32_be_ex(0x12345678, (void**)&ptr); 104 | lwutil_st_u32_be_ex(0x12345678, (void**)&ptr); 105 | TEST_IF_TRUE(arr[0] == 0x12); 106 | TEST_IF_TRUE(arr[1] == 0x34U); 107 | TEST_IF_TRUE(arr[2] == 0x56U); 108 | TEST_IF_TRUE(arr[3] == 0x78U); 109 | TEST_IF_TRUE(arr[4] == 0x12); 110 | TEST_IF_TRUE(arr[5] == 0x34U); 111 | TEST_IF_TRUE(arr[6] == 0x56U); 112 | TEST_IF_TRUE(arr[7] == 0x78U); 113 | TEST_IF_TRUE(arr[8] == 0x00U); 114 | TEST_IF_TRUE(ptr == &arr[8]); 115 | } 116 | /* Test loading integer device */ 117 | { 118 | uint8_t arr[] = {0x12U, 0x34U, 0x56U, 0x78U, 0xDEU, 0xADU, 0xBEU, 0xEFU}; 119 | uint8_t* ptr; 120 | uint32_t u32_1, u32_2; 121 | uint16_t u16_1, u16_2; 122 | 123 | /* Standard version */ 124 | u16_1 = lwutil_ld_u16_le(arr); 125 | TEST_IF_TRUE(u16_1 == 0x3412U); 126 | u16_1 = lwutil_ld_u16_be(arr); 127 | TEST_IF_TRUE(u16_1 == 0x1234U); 128 | u32_1 = lwutil_ld_u32_le(arr); 129 | TEST_IF_TRUE(u32_1 == 0x78563412U); 130 | u32_1 = lwutil_ld_u32_be(arr); 131 | TEST_IF_TRUE(u32_1 == 0x12345678U); 132 | 133 | /* Extended version */ 134 | 135 | /* 16-bit */ 136 | ptr = arr; 137 | u16_1 = lwutil_ld_u16_le_ex((const void**)&ptr); 138 | u16_2 = lwutil_ld_u16_le_ex((const void**)&ptr); 139 | TEST_IF_TRUE(u16_1 == 0x3412U); 140 | TEST_IF_TRUE(u16_2 == 0x7856); 141 | TEST_IF_TRUE(ptr == &arr[4]); 142 | 143 | ptr = arr; 144 | u16_1 = lwutil_ld_u16_be_ex((const void**)&ptr); 145 | u16_2 = lwutil_ld_u16_be_ex((const void**)&ptr); 146 | TEST_IF_TRUE(u16_1 == 0x1234U); 147 | TEST_IF_TRUE(u16_2 == 0x5678); 148 | TEST_IF_TRUE(ptr == &arr[4]); 149 | 150 | /* 32-bit */ 151 | ptr = arr; 152 | u32_1 = lwutil_ld_u32_le_ex((const void**)&ptr); 153 | u32_2 = lwutil_ld_u32_le_ex((const void**)&ptr); 154 | TEST_IF_TRUE(u32_1 == 0x78563412U); 155 | TEST_IF_TRUE(u32_2 == 0xEFBEADDE); 156 | TEST_IF_TRUE(ptr == &arr[8]); 157 | 158 | ptr = arr; 159 | u32_1 = lwutil_ld_u32_be_ex((const void**)&ptr); 160 | u32_2 = lwutil_ld_u32_be_ex((const void**)&ptr); 161 | TEST_IF_TRUE(u32_1 == 0x12345678U); 162 | TEST_IF_TRUE(u32_2 == 0xDEADBEEFU); 163 | TEST_IF_TRUE(ptr == &arr[8]); 164 | } 165 | /* Bit set/reset */ 166 | { 167 | uint32_t val; 168 | val = lwutil_bits_set(0x12340U, 0x01U); 169 | TEST_IF_TRUE(val == 0x12341U); 170 | 171 | val = lwutil_bits_clear(0x12341U, 0x01U); 172 | TEST_IF_TRUE(val == 0x12340U); 173 | 174 | val = lwutil_bits_toggle(0x1234U, 0xFFU); 175 | TEST_IF_TRUE(val == (0x1234U ^ 0xFFU)); 176 | } 177 | /* ASCII operations */ 178 | { 179 | char arr[10]; 180 | lwutil_u32_to_8asciis(0x12345678U, (char*)arr); 181 | TEST_IF_TRUE(strcmp(arr, "12345678") == 0); 182 | lwutil_u32_to_8asciis(0x5678U, (char*)arr); 183 | TEST_IF_TRUE(strcmp(arr, "00005678") == 0); 184 | lwutil_u16_to_4asciis(0x1256U, (char*)arr); 185 | TEST_IF_TRUE(strcmp(arr, "1256") == 0); 186 | lwutil_u16_to_4asciis(0x156U, (char*)arr); 187 | TEST_IF_TRUE(strcmp(arr, "0156") == 0); 188 | lwutil_u8_to_2asciis(0x16U, (char*)arr); 189 | TEST_IF_TRUE(strcmp(arr, "16") == 0); 190 | lwutil_u8_to_2asciis(0x6U, (char*)arr); 191 | TEST_IF_TRUE(strcmp(arr, "06") == 0); 192 | } 193 | /* Test variable integer */ 194 | { 195 | uint8_t arr[10] = {0}; 196 | uint32_t val; 197 | size_t len; 198 | 199 | /* 150 encoded as varint, little endian */ 200 | arr[0] = 0x96U; 201 | arr[1] = 0x01U; 202 | len = lwutil_ld_u32_varint(arr, sizeof(arr), &val); 203 | TEST_IF_TRUE(val == 150); 204 | TEST_IF_TRUE(len == 2); 205 | 206 | /* 86942 encoded as varint, little endian */ 207 | arr[0] = 0x9EU; 208 | arr[1] = 0xA7U; 209 | arr[2] = 0x05U; 210 | len = lwutil_ld_u32_varint(arr, sizeof(arr), &val); 211 | TEST_IF_TRUE(val == 86942U); 212 | TEST_IF_TRUE(len == 3); 213 | 214 | /* 86942 encoded as varint, little endian */ 215 | /* Test array too short */ 216 | arr[0] = 0x9EU; 217 | arr[1] = 0xA7U; 218 | arr[2] = 0x05U; 219 | len = lwutil_ld_u32_varint(arr, 2, &val); 220 | TEST_IF_TRUE(len == 0); 221 | TEST_IF_TRUE(val == 0); 222 | 223 | /* Store value */ 224 | len = lwutil_st_u32_varint(150U, arr, sizeof(arr)); 225 | TEST_IF_TRUE(arr[0] == 0x96); 226 | TEST_IF_TRUE(arr[1] == 0x01); 227 | TEST_IF_TRUE(len == 2); 228 | 229 | /* Store value */ 230 | len = lwutil_st_u32_varint(86942U, arr, sizeof(arr)); 231 | TEST_IF_TRUE(arr[0] == 0x9EU); 232 | TEST_IF_TRUE(arr[1] == 0xA7U); 233 | TEST_IF_TRUE(arr[2] == 0x05U); 234 | TEST_IF_TRUE(len == 3); 235 | 236 | /* Store value */ 237 | len = lwutil_st_u32_varint(86942U, arr, 2); 238 | TEST_IF_TRUE(len == 0); 239 | } 240 | /* Test min max constrain */ 241 | { 242 | uint32_t val; 243 | 244 | val = LWUTIL_MIN(10, 20); 245 | TEST_IF_TRUE(val == 10); 246 | 247 | val = LWUTIL_MAX(10, 20); 248 | TEST_IF_TRUE(val == 20); 249 | 250 | val = LWUTIL_MAX(LWUTIL_MIN(10, 20), 30); 251 | TEST_IF_TRUE(val == 30); 252 | 253 | val = LWUTIL_MIN(LWUTIL_MAX(10, 20), 30); 254 | TEST_IF_TRUE(val == 20); 255 | 256 | val = LWUTIL_CONSTRAIN(10, 20, 30); 257 | TEST_IF_TRUE(val == 20); 258 | 259 | val = LWUTIL_CONSTRAIN(20, 10, 30); 260 | TEST_IF_TRUE(val == 20); 261 | 262 | val = LWUTIL_CONSTRAIN(20, 25, 30); 263 | TEST_IF_TRUE(val == 25); 264 | 265 | val = LWUTIL_CONSTRAIN(20, 35, 30); 266 | TEST_IF_TRUE(val == 30); 267 | } 268 | /* Test map */ 269 | { 270 | uint32_t val; 271 | 272 | /* Map positive scale */ 273 | val = LWUTIL_MAP(10, 5, 15, 50, 100); 274 | TEST_IF_TRUE(val == 75); 275 | 276 | /* Map negative scale */ 277 | val = LWUTIL_MAP(10, 5, 15, 90, 50); 278 | TEST_IF_TRUE(val == 70); 279 | } 280 | return retval; 281 | } -------------------------------------------------------------------------------- /docs/static/dark-light/dark-mode-toggle.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // @license © 2019 Google LLC. Licensed under the Apache License, Version 2.0. 18 | const doc = document; 19 | const store = localStorage; 20 | const PREFERS_COLOR_SCHEME = 'prefers-color-scheme'; 21 | const MEDIA = 'media'; 22 | const LIGHT = 'light'; 23 | const DARK = 'dark'; 24 | const MQ_DARK = `(${PREFERS_COLOR_SCHEME}:${DARK})`; 25 | const MQ_LIGHT = `(${PREFERS_COLOR_SCHEME}:${LIGHT})`; 26 | const LINK_REL_STYLESHEET = 'link[rel=stylesheet]'; 27 | const REMEMBER = 'remember'; 28 | const LEGEND = 'legend'; 29 | const TOGGLE = 'toggle'; 30 | const SWITCH = 'switch'; 31 | const APPEARANCE = 'appearance'; 32 | const PERMANENT = 'permanent'; 33 | const MODE = 'mode'; 34 | const COLOR_SCHEME_CHANGE = 'colorschemechange'; 35 | const PERMANENT_COLOR_SCHEME = 'permanentcolorscheme'; 36 | const ALL = 'all'; 37 | const NOT_ALL = 'not all'; 38 | const NAME = 'dark-mode-toggle'; 39 | const DEFAULT_URL = 'https://googlechromelabs.github.io/dark-mode-toggle/demo/'; 40 | 41 | // See https://html.spec.whatwg.org/multipage/common-dom-interfaces.html ↵ 42 | // #reflecting-content-attributes-in-idl-attributes. 43 | const installStringReflection = (obj, attrName, propName = attrName) => { 44 | Object.defineProperty(obj, propName, { 45 | enumerable: true, 46 | get() { 47 | const value = this.getAttribute(attrName); 48 | return value === null ? '' : value; 49 | }, 50 | set(v) { 51 | this.setAttribute(attrName, v); 52 | }, 53 | }); 54 | }; 55 | 56 | const installBoolReflection = (obj, attrName, propName = attrName) => { 57 | Object.defineProperty(obj, propName, { 58 | enumerable: true, 59 | get() { 60 | return this.hasAttribute(attrName); 61 | }, 62 | set(v) { 63 | if (v) { 64 | this.setAttribute(attrName, ''); 65 | } else { 66 | this.removeAttribute(attrName); 67 | } 68 | }, 69 | }); 70 | }; 71 | 72 | const template = doc.createElement('template'); 73 | // ⚠️ Note: this is a minified version of `src/template-contents.tpl`. 74 | // Compress the CSS with https://cssminifier.com/, then paste it here. 75 | // eslint-disable-next-line max-len 76 | template.innerHTML = `
`; 77 | 78 | export class DarkModeToggle extends HTMLElement { 79 | static get observedAttributes() { 80 | return [MODE, APPEARANCE, PERMANENT, LEGEND, LIGHT, DARK, REMEMBER]; 81 | } 82 | 83 | constructor() { 84 | super(); 85 | 86 | installStringReflection(this, MODE); 87 | installStringReflection(this, APPEARANCE); 88 | installStringReflection(this, LEGEND); 89 | installStringReflection(this, LIGHT); 90 | installStringReflection(this, DARK); 91 | installStringReflection(this, REMEMBER); 92 | 93 | installBoolReflection(this, PERMANENT); 94 | 95 | this._darkCSS = null; 96 | this._lightCSS = null; 97 | 98 | doc.addEventListener(COLOR_SCHEME_CHANGE, (event) => { 99 | this.mode = event.detail.colorScheme; 100 | this._updateRadios(); 101 | this._updateCheckbox(); 102 | }); 103 | 104 | doc.addEventListener(PERMANENT_COLOR_SCHEME, (event) => { 105 | this.permanent = event.detail.permanent; 106 | this._permanentCheckbox.checked = this.permanent; 107 | }); 108 | 109 | this._initializeDOM(); 110 | } 111 | 112 | _initializeDOM() { 113 | const shadowRoot = this.attachShadow({mode: 'open'}); 114 | shadowRoot.appendChild(template.content.cloneNode(true)); 115 | 116 | // We need to support `media="(prefers-color-scheme: dark)"` (with space) 117 | // and `media="(prefers-color-scheme:dark)"` (without space) 118 | this._darkCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${DARK}"]`); 119 | this._lightCSS = doc.querySelectorAll(`${LINK_REL_STYLESHEET}[${MEDIA}*=${PREFERS_COLOR_SCHEME}][${MEDIA}*="${LIGHT}"]`); 120 | 121 | // Get DOM references. 122 | this._lightRadio = shadowRoot.querySelector('[part=lightRadio]'); 123 | this._lightLabel = shadowRoot.querySelector('[part=lightLabel]'); 124 | this._darkRadio = shadowRoot.querySelector('[part=darkRadio]'); 125 | this._darkLabel = shadowRoot.querySelector('[part=darkLabel]'); 126 | this._darkCheckbox = shadowRoot.querySelector('[part=toggleCheckbox]'); 127 | this._checkboxLabel = shadowRoot.querySelector('[part=toggleLabel]'); 128 | this._legendLabel = shadowRoot.querySelector('legend'); 129 | this._permanentAside = shadowRoot.querySelector('aside'); 130 | this._permanentCheckbox = 131 | shadowRoot.querySelector('[part=permanentCheckbox]'); 132 | this._permanentLabel = shadowRoot.querySelector('[part=permanentLabel]'); 133 | 134 | // Does the browser support native `prefers-color-scheme`? 135 | const hasNativePrefersColorScheme = 136 | matchMedia(MQ_DARK).media !== NOT_ALL; 137 | // Listen to `prefers-color-scheme` changes. 138 | if (hasNativePrefersColorScheme) { 139 | matchMedia(MQ_DARK).addListener(({matches}) => { 140 | this.mode = matches ? DARK : LIGHT; 141 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 142 | }); 143 | } 144 | // Set initial state, giving preference to a remembered value, then the 145 | // native value (if supported), and eventually defaulting to a light 146 | // experience. 147 | const rememberedValue = store.getItem(NAME); 148 | if (rememberedValue && [DARK, LIGHT].includes(rememberedValue)) { 149 | this.mode = rememberedValue; 150 | this._permanentCheckbox.checked = true; 151 | this.permanent = true; 152 | } else if (hasNativePrefersColorScheme) { 153 | this.mode = matchMedia(MQ_LIGHT).matches ? LIGHT : DARK; 154 | } 155 | if (!this.mode) { 156 | this.mode = LIGHT; 157 | } 158 | if (this.permanent && !rememberedValue) { 159 | store.setItem(NAME, this.mode); 160 | } 161 | 162 | // Default to toggle appearance. 163 | if (!this.appearance) { 164 | this.appearance = TOGGLE; 165 | } 166 | 167 | // Update the appearance to either of toggle or switch. 168 | this._updateAppearance(); 169 | 170 | // Update the radios 171 | this._updateRadios(); 172 | 173 | // Make the checkbox reflect the state of the radios 174 | this._updateCheckbox(); 175 | 176 | // Synchronize the behavior of the radio and the checkbox. 177 | [this._lightRadio, this._darkRadio].forEach((input) => { 178 | input.addEventListener('change', () => { 179 | this.mode = this._lightRadio.checked ? LIGHT : DARK; 180 | this._updateCheckbox(); 181 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 182 | }); 183 | }); 184 | this._darkCheckbox.addEventListener('change', () => { 185 | this.mode = this._darkCheckbox.checked ? DARK : LIGHT; 186 | this._updateRadios(); 187 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 188 | }); 189 | 190 | // Make remembering the last mode optional 191 | this._permanentCheckbox.addEventListener('change', () => { 192 | this.permanent = this._permanentCheckbox.checked; 193 | this._dispatchEvent(PERMANENT_COLOR_SCHEME, { 194 | permanent: this.permanent, 195 | }); 196 | }); 197 | 198 | // Finally update the mode and let the world know what's going on 199 | this._updateMode(); 200 | this._dispatchEvent(COLOR_SCHEME_CHANGE, {colorScheme: this.mode}); 201 | this._dispatchEvent(PERMANENT_COLOR_SCHEME, { 202 | permanent: this.permanent, 203 | }); 204 | } 205 | 206 | attributeChangedCallback(name, oldValue, newValue) { 207 | if (name === MODE) { 208 | if (![LIGHT, DARK].includes(newValue)) { 209 | throw new RangeError(`Allowed values: "${LIGHT}" and "${DARK}".`); 210 | } 211 | // Only show the dialog programmatically on devices not capable of hover 212 | // and only if there is a label 213 | if (matchMedia('(hover:none)').matches && this.remember) { 214 | this._showPermanentAside(); 215 | } 216 | if (this.permanent) { 217 | store.setItem(NAME, this.mode); 218 | } 219 | this._updateRadios(); 220 | this._updateCheckbox(); 221 | this._updateMode(); 222 | } else if (name === APPEARANCE) { 223 | if (![TOGGLE, SWITCH].includes(newValue)) { 224 | throw new RangeError(`Allowed values: "${TOGGLE}" and "${SWITCH}".`); 225 | } 226 | this._updateAppearance(); 227 | } else if (name === PERMANENT) { 228 | if (this.permanent) { 229 | store.setItem(NAME, this.mode); 230 | } else { 231 | store.removeItem(NAME); 232 | } 233 | this._permanentCheckbox.checked = this.permanent; 234 | } else if (name === LEGEND) { 235 | this._legendLabel.textContent = newValue; 236 | } else if (name === REMEMBER) { 237 | this._permanentLabel.textContent = newValue; 238 | } else if (name === LIGHT) { 239 | this._lightLabel.textContent = newValue; 240 | if (this.mode === LIGHT) { 241 | this._checkboxLabel.textContent = newValue; 242 | } 243 | } else if (name === DARK) { 244 | this._darkLabel.textContent = newValue; 245 | if (this.mode === DARK) { 246 | this._checkboxLabel.textContent = newValue; 247 | } 248 | } 249 | } 250 | 251 | _dispatchEvent(type, value) { 252 | this.dispatchEvent(new CustomEvent(type, { 253 | bubbles: true, 254 | composed: true, 255 | detail: value, 256 | })); 257 | } 258 | 259 | _updateAppearance() { 260 | // Hide or show the light-related affordances dependent on the appearance, 261 | // which can be "switch" or "toggle". 262 | const appearAsToggle = this.appearance === TOGGLE; 263 | this._lightRadio.hidden = appearAsToggle; 264 | this._lightLabel.hidden = appearAsToggle; 265 | this._darkRadio.hidden = appearAsToggle; 266 | this._darkLabel.hidden = appearAsToggle; 267 | this._darkCheckbox.hidden = !appearAsToggle; 268 | this._checkboxLabel.hidden = !appearAsToggle; 269 | } 270 | 271 | _updateRadios() { 272 | if (this.mode === LIGHT) { 273 | this._lightRadio.checked = true; 274 | } else { 275 | this._darkRadio.checked = true; 276 | } 277 | } 278 | 279 | _updateCheckbox() { 280 | if (this.mode === LIGHT) { 281 | this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, 282 | `var(--${NAME}-light-icon,url("${DEFAULT_URL}moon.png"))`); 283 | this._checkboxLabel.textContent = this.light; 284 | if (!this.light) { 285 | this._checkboxLabel.ariaLabel = DARK; 286 | } 287 | this._darkCheckbox.checked = false; 288 | } else { 289 | this._checkboxLabel.style.setProperty(`--${NAME}-checkbox-icon`, 290 | `var(--${NAME}-dark-icon,url("${DEFAULT_URL}sun.png"))`); 291 | this._checkboxLabel.textContent = this.dark; 292 | if (!this.dark) { 293 | this._checkboxLabel.ariaLabel = LIGHT; 294 | } 295 | this._darkCheckbox.checked = true; 296 | } 297 | } 298 | 299 | _updateMode() { 300 | if (this.mode === LIGHT) { 301 | this._lightCSS.forEach((link) => { 302 | link.media = ALL; 303 | link.disabled = false; 304 | }); 305 | this._darkCSS.forEach((link) => { 306 | link.media = NOT_ALL; 307 | link.disabled = true; 308 | }); 309 | } else { 310 | this._darkCSS.forEach((link) => { 311 | link.media = ALL; 312 | link.disabled = false; 313 | }); 314 | this._lightCSS.forEach((link) => { 315 | link.media = NOT_ALL; 316 | link.disabled = true; 317 | }); 318 | } 319 | } 320 | 321 | _showPermanentAside() { 322 | this._permanentAside.style.visibility = 'visible'; 323 | setTimeout(() => { 324 | this._permanentAside.style.visibility = 'hidden'; 325 | }, 3000); 326 | } 327 | } 328 | 329 | customElements.define(NAME, DarkModeToggle); -------------------------------------------------------------------------------- /lwutil/src/include/lwutil/lwutil.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file lwutil.h 3 | * \brief Lightweight utility library 4 | */ 5 | 6 | /* 7 | * Copyright (c) 2024 Tilen MAJERLE 8 | * 9 | * Permission is hereby granted, free of charge, to any person 10 | * obtaining a copy of this software and associated documentation 11 | * files (the "Software"), to deal in the Software without restriction, 12 | * including without limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of the Software, 14 | * and to permit persons to whom the Software is furnished to do so, 15 | * subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * This file is part of LwUTIL - Lightweight utility library. 30 | * 31 | * Author: Tilen MAJERLE 32 | * Version: v1.4.0 33 | */ 34 | #ifndef LWUTIL_HDR_H 35 | #define LWUTIL_HDR_H 36 | 37 | #include 38 | #include 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif /* __cplusplus */ 43 | 44 | /** 45 | * \defgroup LWUTIL Lightweight utility 46 | * \brief Lightweight utility 47 | * \{ 48 | */ 49 | 50 | /** 51 | * \brief Get size of statically allocated array 52 | * Array must be declared in a form of `type var_name[element_count]` 53 | * 54 | * \param[in] x: Object to get array size of 55 | * \return Number of elements in array (`element_count`) 56 | */ 57 | #define LWUTIL_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0])) 58 | 59 | /** 60 | * \brief Get size of statically allocated array 61 | * Array must be declared in a form of `type var_name[element_count]` 62 | * 63 | * \note This is an alias of \ref LWUTIL_ARRAYSIZE 64 | * 65 | * \param[in] x: Object to get array size of 66 | * \return Number of elements in array (`element_count`) 67 | */ 68 | #define LWUTIL_ASZ(x) LWUTIL_ARRAYSIZE(x) 69 | 70 | /** 71 | * \brief Get larger value out of 2 different inputs 72 | * \param[in] x: First input 73 | * \param[in] y: Second input 74 | * \return Larger of both inputs 75 | */ 76 | #define LWUTIL_MAX(x, y) ((x) > (y) ? (x) : (y)) 77 | 78 | /** 79 | * \brief Get smaller value out of 2 different inputs 80 | * \param[in] x: First input 81 | * \param[in] y: Second input 82 | * \return Smaller of both inputs 83 | */ 84 | #define LWUTIL_MIN(x, y) ((x) < (y) ? (x) : (y)) 85 | 86 | /** 87 | * \brief Constrains an input number within a range 88 | * \param[in] x: Number to constrain 89 | * \param[in] a: Minimum allowed number 90 | * \param[in] b: Maximum allowed number 91 | * 92 | * \return `x` if `a < x < b` 93 | * \return `a` if `x <= a` 94 | * \return `b` if `x >= b` 95 | * 96 | * \note Function does not check if `a < b`. This must be handled by the user. 97 | * \note This is implemented as macro and return data type depends on the input number types. 98 | */ 99 | #define LWUTIL_CONSTRAIN(x, a, b) LWUTIL_MIN(LWUTIL_MAX((x), (a)), (b)) 100 | 101 | /** 102 | * \brief Maps the input number with the min and max range to the map of the output min and max range 103 | * 104 | * Mathematical calculation is: 105 | * 106 | * (x - in_min) * (out_max - out_min) 107 | * y = ------------------------------------ + out_min 108 | * (in_max - in_min) 109 | * 110 | * \note Data types depend on the user inputs. If high precision is required, 111 | * user can cast the input variables to appropriate type (double or float), 112 | * or use integer types if decimal precision is not required. 113 | * 114 | * \note Input data is not constrained between out min and out max values. 115 | * This may sometimes be useful for the application. 116 | * Use \ref LWUTIL_CONSTRAIN macro to constrain the value 117 | * 118 | * \param[in] x: Input value to map 119 | * \param[in] in_min: Minimum value to map from (input boundary) 120 | * \param[in] in_max: Maximum value to map from (input boundary) 121 | * \param[in] out_min: Minimum value to map to (output boundary) 122 | * \param[in] out_max: Maximum value to map to (output boundary) 123 | * \return Mapped value 124 | */ 125 | #define LWUTIL_MAP(x, in_min, in_max, out_min, out_max) \ 126 | (((x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min)) 127 | 128 | /** 129 | * \brief Get absolute value of the input 130 | * Returns always-positive value of the input. 131 | * 132 | * \note Special care must be taken when input variable holds 133 | * minimum value available for given signed integer type (char, int, long, ...). 134 | * 135 | * Making absolute value of such input means longer output data type requirement. 136 | * 137 | * Consider writing result of this function to unsigned type of same integer width. 138 | * For example, minimum `signed char` value is `-128` while its absolute value (`128`) 139 | * requires variable type of either `unsigned char` or minimum signed 16-bit (or more). 140 | * 141 | * \param[in] x: Input value 142 | * \return Absolute value of the input value 143 | */ 144 | #define LWUTIL_ABS(x) ((x) < 0 ? -(x) : (x)) 145 | 146 | /** 147 | * \brief Unused variable to avoid compilation warning if declared but not used 148 | * \param[in] x: Input variable to declare unused 149 | */ 150 | #define LWUTIL_UNUSED(x) (void)(x) 151 | 152 | /** 153 | * \brief Dereference the pointer and assign the value to it, 154 | * but only if ptr is not NULL. 155 | * 156 | * \note It is fully up to user to handle to correct variable and data types 157 | * 158 | * \param[in] ptr: Pointer to check and assign to 159 | * \param[in] value: Value to assign 160 | */ 161 | #define LWUTIL_SET_VALUE_IF_PTR_NOT_NULL(ptr, value) \ 162 | do { \ 163 | if ((ptr) != NULL) { \ 164 | *(ptr) = (value); \ 165 | } \ 166 | } while (0) 167 | 168 | #define LWUTIL_CONCAT_BASE(x, y) x##y 169 | #define LWUTIL_CONCAT(s0, s1) LWUTIL_CONCAT_BASE(s0, s1) 170 | 171 | /** 172 | * \brief Compile time assert to validate specific expression. 173 | * Compilation will fail if expression evaluated to zero 174 | * \note Can only be used with the integer types 175 | * 176 | * \param[in] exp: Expression to test. It must be compile-time evaluation 177 | * \param[in] random_variable_name: Meaningful variable name to be used. 178 | * Can be whatever until it is valid variable name 179 | */ 180 | #define LWUTIL_COMPILE_TIME_ASSERT(exp, random_variable_name) \ 181 | typedef char LWUTIL_CONCAT2(random_variable_name, __LINE__)[!(exp) ? -1 : 1]; 182 | 183 | /** 184 | * \brief Check if all bits in the `bit_mask` are set in the input value 185 | * \note Can only be used with the integer types 186 | * 187 | * \param[in] val: Value to check for bits in 188 | * \param[in] bit_mask: Bit mask to check in value 189 | * \return `1` if all bits are set, `0` otherwise 190 | */ 191 | #define lwutil_bits_is_set_all(val, bit_mask) (((val) & (bit_mask)) == (bit_mask)) 192 | 193 | /** 194 | * \brief Check if any of the `bit_mask` bits is set in the input value 195 | * \note Can only be used with the integer types 196 | * 197 | * \param[in] val: Value to check for bits in 198 | * \param[in] bit_mask: Bit mask to check in value 199 | * \return `1` if any bit is set, `0` otherwise 200 | */ 201 | #define lwutil_bits_is_set_any(val, bit_mask) (((val) & (bit_mask)) != 0) 202 | 203 | /** 204 | * \brief Set bit mask in the input value 205 | * \note Can only be used with the integer types 206 | * 207 | * \param[in] val: Value to set bits in. 208 | * Original input is not modified. It is pass-by-value. 209 | * \param[in] bit_mask: Bit mask indicating which bits to set 210 | * \return New value with bitwise OR between input value and bit mask 211 | */ 212 | #define lwutil_bits_set(val, bit_mask) ((val) | (bit_mask)) 213 | 214 | /** 215 | * \brief Clear bit mask in the input value 216 | * \note Can only be used with the integer types 217 | * 218 | * \param[in] val: Value to clear bits in. 219 | * Original input is not modified. It is pass-by-value. 220 | * \param[in] bit_mask: Bit mask indicating which bits to clear 221 | * \return New value with bitwise AND and negated bit_mask between input value and bit mask 222 | * Value has bits cleared in the bit_mask set 223 | */ 224 | #define lwutil_bits_clear(val, bit_mask) ((val) & ~(bit_mask)) 225 | 226 | /** 227 | * \brief Toggle bit mask in the input value 228 | * \note Can only be used with the integer types 229 | * 230 | * \param[in] val: Value to toggle bits in. 231 | * Original input is not modified. It is pass-by-value. 232 | * \param[in] bit_mask: Bit mask indicating which bits to toggle 233 | * \return New value with bitwise AND and negated bit_mask between input value and bit mask 234 | * Value has bits cleared in the bit_mask set 235 | */ 236 | #define lwutil_bits_toggle(val, bit_mask) ((val) ^ (bit_mask)) 237 | 238 | /** 239 | * \brief Store `16-bit` value to bytes array in little-endian format 240 | * \param[in] val: Value to write to output array 241 | * \param[out] ptr: Minimum `2-bytes` long output array to write value to 242 | */ 243 | static inline void 244 | lwutil_st_u16_le(uint16_t val, void* ptr) { 245 | uint8_t* p = (uint8_t*)ptr; 246 | 247 | p[0] = (uint8_t)((val >> 0) & 0xFF); 248 | p[1] = (uint8_t)((val >> 8) & 0xFF); 249 | } 250 | 251 | /** 252 | * \brief Store `32-bit` value to bytes array in little-endian format 253 | * \param[in] val: Value to write to output array 254 | * \param[out] ptr: Minimum `4-bytes` long output array to write value to 255 | */ 256 | static inline void 257 | lwutil_st_u32_le(uint32_t val, void* ptr) { 258 | uint8_t* p = (uint8_t*)ptr; 259 | 260 | p[0] = (uint8_t)((val >> 0) & 0xFF); 261 | p[1] = (uint8_t)((val >> 8) & 0xFF); 262 | p[2] = (uint8_t)((val >> 16) & 0xFF); 263 | p[3] = (uint8_t)((val >> 24) & 0xFF); 264 | } 265 | 266 | /** 267 | * \brief Load `16-bit` value from bytes array in little-endian format 268 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 269 | * \return `16-bit` value extracted from input array 270 | */ 271 | static inline uint16_t 272 | lwutil_ld_u16_le(const void* ptr) { 273 | const uint8_t* p = (const uint8_t*)ptr; 274 | return p[1] << 8 | p[0]; 275 | } 276 | 277 | /** 278 | * \brief Load `32-bit` value from bytes array in little-endian format 279 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 280 | * \return `32-bit` value extracted from input array 281 | */ 282 | static inline uint32_t 283 | lwutil_ld_u32_le(const void* ptr) { 284 | const uint8_t* p = (const uint8_t*)ptr; 285 | return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; 286 | } 287 | 288 | /** 289 | * \brief Store `16-bit` value to bytes array in big-endian format 290 | * \param[in] val: Value to write to output array 291 | * \param[out] ptr: Minimum `2-bytes` long output array to write value to 292 | */ 293 | static inline void 294 | lwutil_st_u16_be(uint16_t val, void* ptr) { 295 | uint8_t* p = (uint8_t*)ptr; 296 | 297 | p[0] = (uint8_t)((val >> 8) & 0xFF); 298 | p[1] = (uint8_t)((val >> 0) & 0xFF); 299 | } 300 | 301 | /** 302 | * \brief Store `32-bit` value to bytes array in big-endian format 303 | * \param[in] val: Value to write to output array 304 | * \param[out] ptr: Minimum `4-bytes` long output array to write value to 305 | */ 306 | static inline void 307 | lwutil_st_u32_be(uint32_t val, void* ptr) { 308 | uint8_t* p = (uint8_t*)ptr; 309 | 310 | p[0] = (uint8_t)((val >> 24) & 0xFF); 311 | p[1] = (uint8_t)((val >> 16) & 0xFF); 312 | p[2] = (uint8_t)((val >> 8) & 0xFF); 313 | p[3] = (uint8_t)((val >> 0) & 0xFF); 314 | } 315 | 316 | /** 317 | * \brief Load `16-bit` value from bytes array in big-endian format 318 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 319 | * \return `16-bit` value extracted from input array 320 | */ 321 | static inline uint16_t 322 | lwutil_ld_u16_be(const void* ptr) { 323 | const uint8_t* p = (const uint8_t*)ptr; 324 | return p[0] << 8 | p[1]; 325 | } 326 | 327 | /** 328 | * \brief Load `32-bit` value from bytes array in big-endian format 329 | * \param[in] ptr: Minimum `4-bytes` long input array to extract bytes from 330 | * \return `32-bit` value extracted from input array 331 | */ 332 | static inline uint32_t 333 | lwutil_ld_u32_be(const void* ptr) { 334 | const uint8_t* p = (const uint8_t*)ptr; 335 | return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 336 | } 337 | 338 | /** 339 | * \brief Store `16-bit` value to bytes array in little-endian format 340 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 341 | * Input pointer will point after the used bytes 342 | * \param[in] val: Value to write to output array 343 | * \param[out] ptr: Minimum `2-bytes` long output array to write value to 344 | */ 345 | static inline void 346 | lwutil_st_u16_le_ex(uint16_t val, void** ptr) { 347 | uint8_t* p = (uint8_t*)(*ptr); 348 | 349 | p[0] = (uint8_t)((val >> 0) & 0xFF); 350 | p[1] = (uint8_t)((val >> 8) & 0xFF); 351 | *ptr = (uint8_t*)(*ptr) + 2; 352 | } 353 | 354 | /** 355 | * \brief Store `32-bit` value to bytes array in little-endian format 356 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 357 | * Input pointer will point after the used bytes 358 | * \param[in] val: Value to write to output array 359 | * \param[out] ptr: Minimum `4-bytes` long output array to write value to 360 | */ 361 | static inline void 362 | lwutil_st_u32_le_ex(uint32_t val, void** ptr) { 363 | uint8_t* p = (uint8_t*)(*ptr); 364 | 365 | p[0] = (uint8_t)((val >> 0) & 0xFF); 366 | p[1] = (uint8_t)((val >> 8) & 0xFF); 367 | p[2] = (uint8_t)((val >> 16) & 0xFF); 368 | p[3] = (uint8_t)((val >> 24) & 0xFF); 369 | *ptr = (uint8_t*)(*ptr) + 4; 370 | } 371 | 372 | /** 373 | * \brief Load `16-bit` value from bytes array in little-endian format 374 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 375 | * Input pointer will point after the used bytes 376 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 377 | * \return `16-bit` value extracted from input array 378 | */ 379 | static inline uint16_t 380 | lwutil_ld_u16_le_ex(const void** ptr) { 381 | const uint8_t* p = (const uint8_t*)(*ptr); 382 | *ptr = (uint8_t*)(*ptr) + 2; 383 | return p[1] << 8 | p[0]; 384 | } 385 | 386 | /** 387 | * \brief Load `32-bit` value from bytes array in little-endian format 388 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 389 | * Input pointer will point after the used bytes 390 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 391 | * \return `32-bit` value extracted from input array 392 | */ 393 | static inline uint32_t 394 | lwutil_ld_u32_le_ex(const void** ptr) { 395 | const uint8_t* p = (const uint8_t*)(*ptr); 396 | *ptr = (uint8_t*)(*ptr) + 4; 397 | return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; 398 | } 399 | 400 | /** 401 | * \brief Store `16-bit` value to bytes array in big-endian format 402 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 403 | * Input pointer will point after the used bytes 404 | * \param[in] val: Value to write to output array 405 | * \param[out] ptr: Minimum `2-bytes` long output array to write value to 406 | */ 407 | static inline void 408 | lwutil_st_u16_be_ex(uint16_t val, void** ptr) { 409 | uint8_t* p = (uint8_t*)(*ptr); 410 | 411 | p[0] = (uint8_t)((val >> 8) & 0xFF); 412 | p[1] = (uint8_t)((val >> 0) & 0xFF); 413 | *ptr = (uint8_t*)(*ptr) + 2; 414 | } 415 | 416 | /** 417 | * \brief Store `32-bit` value to bytes array in big-endian format 418 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 419 | * Input pointer will point after the used bytes 420 | * \param[in] val: Value to write to output array 421 | * \param[out] ptr: Minimum `4-bytes` long output array to write value to 422 | */ 423 | static inline void 424 | lwutil_st_u32_be_ex(uint32_t val, void** ptr) { 425 | uint8_t* p = (uint8_t*)(*ptr); 426 | 427 | p[0] = (uint8_t)((val >> 24) & 0xFF); 428 | p[1] = (uint8_t)((val >> 16) & 0xFF); 429 | p[2] = (uint8_t)((val >> 8) & 0xFF); 430 | p[3] = (uint8_t)((val >> 0) & 0xFF); 431 | *ptr = (uint8_t*)(*ptr) + 4; 432 | } 433 | 434 | /** 435 | * \brief Load `16-bit` value from bytes array in big-endian format 436 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 437 | * Input pointer will point after the used bytes 438 | * \param[in] ptr: Minimum `2-bytes` long input array to extract bytes from 439 | * \return `16-bit` value extracted from input array 440 | */ 441 | static inline uint16_t 442 | lwutil_ld_u16_be_ex(const void** ptr) { 443 | const uint8_t* p = (const uint8_t*)(*ptr); 444 | *ptr = (uint8_t*)(*ptr) + 2; 445 | return p[0] << 8 | p[1]; 446 | } 447 | 448 | /** 449 | * \brief Load `32-bit` value from bytes array in big-endian format 450 | * \note Extended version, accepts the pointer to pointer and modifies the address pointer points to. 451 | * Input pointer will point after the used bytes 452 | * \param[in] ptr: Minimum `4-bytes` long input array to extract bytes from 453 | * \return `32-bit` value extracted from input array 454 | */ 455 | static inline uint32_t 456 | lwutil_ld_u32_be_ex(const void** ptr) { 457 | const uint8_t* p = (const uint8_t*)(*ptr); 458 | *ptr = (uint8_t*)(*ptr) + 4; 459 | return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 460 | } 461 | 462 | void lwutil_u8_to_2asciis(uint8_t hex, char* ascii); 463 | void lwutil_u16_to_4asciis(uint16_t hex, char* ascii); 464 | void lwutil_u32_to_8asciis(uint32_t hex, char* ascii); 465 | uint8_t lwutil_ld_u32_varint(const void* ptr, size_t ptr_len, uint32_t* val_out); 466 | uint8_t lwutil_st_u32_varint(uint32_t val, void* ptr, size_t ptr_len); 467 | 468 | /** 469 | * \} 470 | */ 471 | 472 | #ifdef __cplusplus 473 | } 474 | #endif /* __cplusplus */ 475 | 476 | #endif /* LWUTIL_HDR_H */ 477 | --------------------------------------------------------------------------------