From 4587ba427acefaddb50e327f786095e6b3856e81 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 8 Jul 2026 13:26:13 +0200 Subject: [PATCH 01/26] Add `.proto` file --- src/Debug/debug.proto | 182 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/Debug/debug.proto diff --git a/src/Debug/debug.proto b/src/Debug/debug.proto new file mode 100644 index 000000000..a9cc1cb8b --- /dev/null +++ b/src/Debug/debug.proto @@ -0,0 +1,182 @@ +//// WARDUINO DEBUG API + +syntax = "proto3"; + +package debug; + +option optimize_for = LITE_RUNTIME; +option cc_enable_arenas = false; + +//// + +enum State { + WARDUINOrun = 0; + WARDUINOpause = 1; + WARDUINOstep = 2; + PROXYrun = 3; + PROXYhalt = 4; +} + +enum Command { + // standard debug operations + run = 0; + halt = 1; + pause = 2; + step = 3; + bpadd = 4; + bprem = 5; + + // inspection operations + dump = 6; + dumplocals = 7; + snapshot = 8; + dumpevents = 9; + dumpcallbacks = 10; + + // update operations + updatefunc = 11; + updatelocal = 12; + updatecallbacks = 13; + + // edward debug operations + loadsnapshot = 14; + proxify = 15; + proxyadd = 16; + proxyrem = 17; + proxycall = 18; + popevent = 19; + pushevent = 20; +} + +//// 2 types of messages: DebugMessage or Notification: + +message DebugMessage { // messages FROM frontend + Command command = 1; + + // optional payload + optional Payload payload = 2; +} + +message Notification { // messages TO frontend + enum Type { + // simple notifications: + continued = 0; + halted = 1; + paused = 2; + stepped = 3; + hitbreakpoint = 4; + newevent = 5; // a new event got pushed from proxy + changeaffected = 11; + + // payload responses: + dump = 6; + dumplocals = 7; + snapshot = 8; + dumpevents = 9; + dumpcallbacks = 10; + + // errors: + malformed = 12; // received message was malformed + unknown = 13; // received unknown command + } + + Type type = 1; + optional Payload payload = 2; +} + +//// content of messages: + +message Payload { + oneof payload { + Snapshot snapshot = 1; // dumps + Locals locals = 2; // dumplocals + Function function = 3; // dumps, updatefunc, proxyadd/rem + uint32 breakpoint = 4; // bpadd, bprem + EventsQueue queue = 5; // dumps + Event event = 6; // event operations + Range range = 7; // dump eventsqueue + CallbackMapping callbacks = 8; // dump/update callbacks + RFC call = 9; // proxycall + } +} + +message Snapshot { + // minimal state fields + uint32 program_counter = 1; + State state = 2; + repeated uint32 breakpoints = 3; + repeated Function functions = 4; + repeated CallstackEntry callstack = 5; + + // optional extending state fields + optional Locals locals = 6; + optional EventsQueue queue = 7; + optional CallbackMapping callbacks = 8; +} + +message Function { + int32 fidx = 1; + + // optional fields for updating: + Range range = 2; // from, to + Locals locals = 3; + bytes instructions = 4; // implementation +} + +message RFC { // Remote Function Call + int32 fidx = 1; + int32 argcount = 2; + repeated Value args = 3; +} + +message CallstackEntry { + int32 type = 1; + int32 fidx = 2; + int32 sp = 3; + int32 fp = 4; + int32 start = 5; + int32 ra = 6; +} + +message Locals { + int32 count = 1; + repeated Value values = 2; +} + +message Value { + enum Type { + i32 = 0; + i64 = 1; + f32 = 2; + f64 = 3; + any = 4; + } + Type type = 1; + string value = 2; // can be any wasm value + int32 index = 3; +} + +message CallbackMapping { + repeated CallbackEntry entries = 1; +} + +message CallbackEntry { + string topic = 1; + repeated int32 tidx = 2; +} + +message EventsQueue { + int32 count = 1; // total count of events in queue + repeated Event events = 2; // slice of events + optional Range range = 3; // range of returned slice +} + +message Event { + string topic = 1; + string payload = 2; +} + +message Range { + int32 start = 1; // start index + int32 end = 2; // end index +} From b2ae8f72f0ccdb884285942161ba3d103d764ad4 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 8 Jul 2026 13:59:36 +0200 Subject: [PATCH 02/26] Add nanopb to Cmake --- CMakeLists.txt | 6 +- lib/FindNanopb.cmake | 480 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 lib/FindNanopb.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e560ba033..989035a68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,8 +39,12 @@ configure_file(src/config.h.in include/warduino/config.h) # Build the emulator version of WARDuino if (BUILD_EMULATOR) set(EXTERNAL_LIB_HEADERS lib/json/single_include) + set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) find_package(Threads REQUIRED) + find_package(Nanopb REQUIRED) + + nanopb_generate_cpp(TARGET proto src/Debug/debug.proto) set(SOURCE_FILES src/WARDuino/WARDuino.cpp @@ -71,7 +75,7 @@ if (BUILD_EMULATOR) # WARDuino CLI add_executable(wdcli platforms/CLI-Emulator/main.cpp ${SOURCE_FILES}) - target_link_libraries(wdcli PRIVATE Threads::Threads) + target_link_libraries(wdcli PRIVATE Threads::Threads proto) target_include_directories(wdcli PRIVATE ${EXTERNAL_LIB_HEADERS} "${PROJECT_BINARY_DIR}/include") endif (BUILD_EMULATOR) diff --git a/lib/FindNanopb.cmake b/lib/FindNanopb.cmake new file mode 100644 index 000000000..81229f188 --- /dev/null +++ b/lib/FindNanopb.cmake @@ -0,0 +1,480 @@ +# This is an example script for use with CMake projects for locating and configuring +# the nanopb library. +# +# The following variables can be set and are optional: +# +# +# PROTOBUF_SRC_ROOT_FOLDER - When compiling with MSVC, if this cache variable is set +# the protobuf-default VS project build locations +# (vsprojects/Debug & vsprojects/Release) will be searched +# for libraries and binaries. +# +# NANOPB_IMPORT_DIRS - List of additional directories to be searched for +# imported .proto files. +# +# NANOPB_OPTIONS - List of options passed to nanopb. +# +# Nanopb_FIND_COMPONENTS - List of options to append to NANOPB_OPTIONS without the +# leading '--'. This should not manually be set, but allows +# passing options to nanopb via find_package. For example, +# 'find_package(Nanopb REQUIRED COMPONENTS cpp-descriptors)' +# is equivalent to setting NANOPB_OPTIONS to --cpp-descriptors. +# +# NANOPB_DEPENDS - List of files to be used as dependencies +# for the generated source and header files. These +# files are not directly passed as options to +# nanopb but rather their directories. +# +# NANOPB_GENERATE_CPP_APPEND_PATH - By default -I will be passed to protoc +# for each directory where a proto file is referenced. +# This causes all output files to go directly +# under build directory, instead of mirroring +# relative paths of source directories. +# Set to FALSE if you want to disable this behaviour. +# PROTOC_OPTIONS - Pass options to protoc executable +# +# Defines the following variables: +# +# NANOPB_FOUND - Found the nanopb library (source&header files, generator tool, protoc compiler tool) +# NANOPB_INCLUDE_DIRS - Include directories for Google Protocol Buffers +# +# The following cache variables are also available to set or use: +# PROTOBUF_PROTOC_EXECUTABLE - The protoc compiler +# NANOPB_GENERATOR_SOURCE_DIR - The nanopb generator source +# +# ==================================================================== +# +# NANOPB_GENERATE_CPP (public function) +# NANOPB_GENERATE_CPP(SRCS HDRS [RELPATH ] +# ...) +# SRCS = Variable to define with autogenerated source files +# HDRS = Variable to define with autogenerated header files +# NANOPB_GENERATE_CPP(TARGET TGT [RELPATH ] +# ...) +# TGT = Name of the static library to create with the autogenerated files +# +# If you want to use relative paths in your import statements use the RELPATH +# option. The argument to RELPATH should be the directory that all the +# imports will be relative to. +# When RELPATH is not specified then all proto files can be imported without +# a path. +# +# +# ==================================================================== +# Example using modern targets: +# +# set(NANOPB_SRC_ROOT_FOLDER "/path/to/nanopb") +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${NANOPB_SRC_ROOT_FOLDER}/extra) +# find_package( Nanopb REQUIRED ) +# +# NANOPB_GENERATE_CPP(TARGET proto foo.proto) +# +# add_executable(bar bar.cc) +# target_link_libraries(bar proto) +# +# Example with RELPATH: +# Assume we have a layout like: +# .../CMakeLists.txt +# .../bar.cc +# .../proto/ +# .../proto/foo.proto (Which contains: import "sub/bar.proto"; ) +# .../proto/sub/bar.proto +# Everything would be the same as the previous example, but the call to +# NANOPB_GENERATE_CPP would change to: +# +# NANOPB_GENERATE_CPP(TARGET proto RELPATH proto +# proto/foo.proto proto/sub/bar.proto) +# +# Example using traditional variables: +# +# set(NANOPB_SRC_ROOT_FOLDER "/path/to/nanopb") +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${NANOPB_SRC_ROOT_FOLDER}/extra) +# find_package( Nanopb REQUIRED ) +# include_directories(${NANOPB_INCLUDE_DIRS}) +# +# NANOPB_GENERATE_CPP(PROTO_SRCS PROTO_HDRS foo.proto) +# +# include_directories(${CMAKE_CURRENT_BINARY_DIR}) +# add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS}) +# +# ==================================================================== + +#============================================================================= +# Copyright 2009 Kitware, Inc. +# Copyright 2009-2011 Philip Lowman +# Copyright 2008 Esben Mose Hansen, Ange Optimization ApS +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +#============================================================================= +# +# Changes +# 2013.01.31 - Pavlo Ilin - used Modules/FindProtobuf.cmake from cmake 2.8.10 to +# write FindNanopb.cmake +# +#============================================================================= + + +function(NANOPB_GENERATE_CPP) + cmake_parse_arguments(NANOPB_GENERATE_CPP "" "RELPATH;TARGET" "" ${ARGN}) + if(NANOPB_GENERATE_CPP_TARGET) + set(SRCS NANOPB_TARGET_SRCS) + set(HDRS NANOPB_TARGET_HDRS) + else() + list(GET NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS 0 SRCS) + list(GET NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS 1 HDRS) + list(REMOVE_AT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS 0 1) + endif() + if(NOT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS) + return() + endif() + set(NANOPB_OPTIONS_DIRS) + + if(MSVC) + set(CUSTOM_COMMAND_PREFIX call) + endif() + + if(NANOPB_GENERATE_CPP_RELPATH) + get_filename_component(NANOPB_GENERATE_CPP_RELPATH ${NANOPB_GENERATE_CPP_RELPATH} ABSOLUTE) + list(APPEND _nanopb_include_path "-I${NANOPB_GENERATE_CPP_RELPATH}") + list(APPEND NANOPB_OPTIONS_DIRS ${NANOPB_GENERATE_CPP_RELPATH}) + endif() + + if(NANOPB_GENERATE_CPP_APPEND_PATH) + # Create an include path for each file specified + foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(ABS_PATH ${ABS_FIL} PATH) + list(APPEND _nanopb_include_path "-I${ABS_PATH}") + endforeach() + else() + list(APPEND _nanopb_include_path "-I${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + if(DEFINED NANOPB_IMPORT_DIRS) + foreach(DIR ${NANOPB_IMPORT_DIRS}) + get_filename_component(ABS_PATH ${DIR} ABSOLUTE) + list(APPEND _nanopb_include_path "-I${ABS_PATH}") + endforeach() + endif() + + list(REMOVE_DUPLICATES _nanopb_include_path) + + set(GENERATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/nanopb/generator) + + set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py) + if (CMAKE_HOST_WIN32) + set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb.bat) + else() + set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb) + endif() + + set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto) + set(GENERATOR_CORE_SRC + ${GENERATOR_CORE_DIR}/nanopb.proto) + + # Set extensions according to NANOPB_OPTIONS + string(REGEX MATCH "--extension=[^ ]+" _gen_ext "${NANOPB_OPTIONS}") + string(REGEX MATCH "--header-extension=[^ ]+" _gen_hdr_ext + "${NANOPB_OPTIONS}") + string(REGEX MATCH "--source-extension=[^ ]+" _gen_src_ext + "${NANOPB_OPTIONS}") + if(_gen_ext) + string(REPLACE "--extension=" "" GEN_EXTENSION "${_gen_ext}") + else() + set(GEN_EXTENSION ".pb") + endif() + if(_gen_hdr_ext) + string(REPLACE "--header-extension=" "" GEN_HDR_EXTENSION "${_gen_hdr_ext}") + else() + set(GEN_HDR_EXTENSION ".h") + endif() + if(_gen_src_ext) + string(REPLACE "--source-extension=" "" GEN_SRC_EXTENSION "${_gen_src_ext}") + else() + set(GEN_SRC_EXTENSION ".c") + endif() + + # Treat the source directory as immutable. + # + # Copy the generator directory to the build directory before + # compiling python and proto files. Fixes issues when using the + # same build directory with different python/protobuf versions + # as the binary build directory is discarded across builds. + # + # Notice: copy_directory does not copy the content if the directory already exists. + # We therefore append '/' to specify that we want to copy the content of the folder. See #847 + # + add_custom_command( + OUTPUT ${NANOPB_GENERATOR_EXECUTABLE} ${GENERATOR_CORE_SRC} + COMMAND ${CMAKE_COMMAND} -E copy_directory + ARGS ${NANOPB_GENERATOR_SOURCE_DIR}/ ${GENERATOR_PATH} + VERBATIM) + + set(GENERATOR_CORE_PYTHON_SRC) + foreach(FIL ${GENERATOR_CORE_SRC}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(FIL_WE ${FIL} NAME_WE) + + set(output "${GENERATOR_CORE_DIR}/${FIL_WE}_pb2.py") + set(GENERATOR_CORE_PYTHON_SRC ${GENERATOR_CORE_PYTHON_SRC} ${output}) + add_custom_command( + OUTPUT ${output} + COMMAND ${CUSTOM_COMMAND_PREFIX} ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS -I${GENERATOR_PATH}/proto + --python_out=${GENERATOR_CORE_DIR} ${ABS_FIL} + DEPENDS ${ABS_FIL} + VERBATIM) + endforeach() + + foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS}) + get_filename_component(ABS_FIL ${FIL} ABSOLUTE) + get_filename_component(FIL_WE ${FIL} NAME_WLE) + get_filename_component(FIL_DIR ${ABS_FIL} PATH) + set(FIL_PATH_REL) + if(NANOPB_GENERATE_CPP_RELPATH) + # Check that the file is under the given "RELPATH" + string(FIND ${ABS_FIL} ${NANOPB_GENERATE_CPP_RELPATH} LOC) + if (${LOC} EQUAL 0) + string(REPLACE "${NANOPB_GENERATE_CPP_RELPATH}/" "" FIL_REL ${ABS_FIL}) + get_filename_component(FIL_PATH_REL ${FIL_REL} PATH) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}) + endif() + endif() + if(NOT FIL_PATH_REL) + set(FIL_PATH_REL ".") + endif() + + list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}${GEN_EXTENSION}${GEN_SRC_EXTENSION}") + list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}${GEN_EXTENSION}${GEN_HDR_EXTENSION}") + + get_filename_component(ABS_OPT_IN_FIL ${FIL_DIR}/${FIL_WE}.options.in ABSOLUTE) + if(EXISTS ${ABS_OPT_IN_FIL}) + set(ABS_OPT_FIL "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.options") + configure_file(${ABS_OPT_IN_FIL} ${ABS_OPT_FIL}) + else() + get_filename_component(ABS_OPT_FIL ${FIL_DIR}/${FIL_WE}.options ABSOLUTE) + endif() + + # If there an options file in the same working directory, set it as a dependency + if(EXISTS ${ABS_OPT_FIL}) + # Get directory as lookups for dependency options fail if an options + # file is used. The options is still set as a dependency of the + # generated source and header. + get_filename_component(options_dir ${ABS_OPT_FIL} DIRECTORY) + list(APPEND NANOPB_OPTIONS_DIRS ${options_dir}) + else() + set(ABS_OPT_FIL) + endif() + + # If the dependencies are options files, we need to pass the directories + # as arguments to nanopb + foreach(depends_file ${NANOPB_DEPENDS}) + get_filename_component(ext ${depends_file} EXT) + if(ext STREQUAL ".options") + get_filename_component(depends_dir ${depends_file} DIRECTORY) + list(APPEND NANOPB_OPTIONS_DIRS ${depends_dir}) + endif() + endforeach() + + if(NANOPB_OPTIONS_DIRS) + list(REMOVE_DUPLICATES NANOPB_OPTIONS_DIRS) + endif() + + set(NANOPB_PLUGIN_OPTIONS) + foreach(options_path ${NANOPB_OPTIONS_DIRS}) + set(NANOPB_PLUGIN_OPTIONS "${NANOPB_PLUGIN_OPTIONS} -I${options_path}") + endforeach() + + # Remove leading space before the first -I directive + string(STRIP "${NANOPB_PLUGIN_OPTIONS}" NANOPB_PLUGIN_OPTIONS) + + if(NANOPB_OPTIONS) + set(NANOPB_PLUGIN_OPTIONS "${NANOPB_PLUGIN_OPTIONS} ${NANOPB_OPTIONS}") + endif() + + # based on the version of protoc it might be necessary to add "/${FIL_PATH_REL}" currently dealt with in #516 + set(NANOPB_OUT "${CMAKE_CURRENT_BINARY_DIR}") + + # We need to pass the path to the option files to the nanopb plugin. There are two ways to do it. + # - An older hacky one using ':' as option separator in protoc args preventing the ':' to be used in path. + # - Or a newer one, using --nanopb_opt which requires a version of protoc >= 3.6 + # Since nanopb 0.4.6, --nanopb_opt is the default. + if(DEFINED NANOPB_PROTOC_OLDER_THAN_3_6_0) + set(NANOPB_OPT_STRING "--nanopb_out=${NANOPB_PLUGIN_OPTIONS}:${NANOPB_OUT}") + else() + set(NANOPB_OPT_STRING "--nanopb_opt=${NANOPB_PLUGIN_OPTIONS}" "--nanopb_out=${NANOPB_OUT}") + endif() + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}${GEN_EXTENSION}${GEN_SRC_EXTENSION}" + "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}${GEN_EXTENSION}${GEN_HDR_EXTENSION}" + COMMAND ${CUSTOM_COMMAND_PREFIX} ${PROTOBUF_PROTOC_EXECUTABLE} + ARGS ${_nanopb_include_path} -I${GENERATOR_PATH} + -I${GENERATOR_CORE_DIR} -I${CMAKE_CURRENT_BINARY_DIR} + --plugin=protoc-gen-nanopb=${NANOPB_GENERATOR_PLUGIN} + ${NANOPB_OPT_STRING} + ${PROTOC_OPTIONS} + ${ABS_FIL} + DEPENDS ${ABS_FIL} ${GENERATOR_CORE_PYTHON_SRC} + ${ABS_OPT_FIL} ${NANOPB_DEPENDS} + COMMENT "Running C++ protocol buffer compiler using nanopb plugin on ${FIL}" + VERBATIM ) + + endforeach() + + set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE) + + if(NANOPB_GENERATE_CPP_TARGET) + add_library(${NANOPB_GENERATE_CPP_TARGET} STATIC EXCLUDE_FROM_ALL ${${SRCS}} ${${HDRS}}) + target_include_directories(${NANOPB_GENERATE_CPP_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(${NANOPB_GENERATE_CPP_TARGET} nanopb) + endif() + + if(NOT DEFINED NANOPB_GENERATE_CPP_STANDALONE) + set(NANOPB_GENERATE_CPP_STANDALONE TRUE) + endif() + + if(MSVC) + unset(CUSTOM_COMMAND_PREFIX) + endif() + + if(NOT NANOPB_GENERATE_CPP_TARGET) + if (NANOPB_GENERATE_CPP_STANDALONE) + set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE) + set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE) + else() + set(${SRCS} ${${SRCS}} PARENT_SCOPE) + set(${HDRS} ${${HDRS}} PARENT_SCOPE) + endif() + endif() +endfunction() + + + +# +# Main. +# + +# By default have NANOPB_GENERATE_CPP macro pass -I to protoc +# for each directory where a proto file is referenced. +if(NOT DEFINED NANOPB_GENERATE_CPP_APPEND_PATH) + set(NANOPB_GENERATE_CPP_APPEND_PATH TRUE) +endif() + +# Make a really good guess regarding location of NANOPB_SRC_ROOT_FOLDER +if(NOT DEFINED NANOPB_SRC_ROOT_FOLDER) + get_filename_component(NANOPB_SRC_ROOT_FOLDER + ${CMAKE_CURRENT_LIST_DIR}/.. ABSOLUTE) +endif() + +# Parse any options given to find_package(... COMPONENTS ...) +foreach(component ${Nanopb_FIND_COMPONENTS}) + list(APPEND NANOPB_OPTIONS "--${component}") +endforeach() + +# Find the include directory +find_path(NANOPB_INCLUDE_DIRS + pb.h + PATHS ${NANOPB_SRC_ROOT_FOLDER} + NO_CMAKE_FIND_ROOT_PATH +) +mark_as_advanced(NANOPB_INCLUDE_DIRS) + +# Find nanopb source files +set(NANOPB_SRCS) +set(NANOPB_HDRS) +list(APPEND _nanopb_srcs pb_decode.c pb_encode.c pb_common.c) +list(APPEND _nanopb_hdrs pb_decode.h pb_encode.h pb_common.h pb.h) + +foreach(FIL ${_nanopb_srcs}) + find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_SRC_ROOT_FOLDER} ${NANOPB_INCLUDE_DIRS} NO_CMAKE_FIND_ROOT_PATH) + list(APPEND NANOPB_SRCS "${${FIL}__nano_pb_file}") + mark_as_advanced(${FIL}__nano_pb_file) +endforeach() + +foreach(FIL ${_nanopb_hdrs}) + find_file(${FIL}__nano_pb_file NAMES ${FIL} PATHS ${NANOPB_INCLUDE_DIRS} NO_CMAKE_FIND_ROOT_PATH) + mark_as_advanced(${FIL}__nano_pb_file) + list(APPEND NANOPB_HDRS "${${FIL}__nano_pb_file}") +endforeach() + +# Create the library target +add_library(nanopb STATIC EXCLUDE_FROM_ALL ${NANOPB_SRCS}) +target_compile_features(nanopb PUBLIC c_std_11) +target_include_directories(nanopb PUBLIC ${NANOPB_INCLUDE_DIRS}) + +# Find the local protoc Executable +find_program(PROTOBUF_PROTOC_EXECUTABLE + NAMES protoc + DOC "The Google Protocol Buffers Compiler" + PATHS + ${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Release + ${PROTOBUF_SRC_ROOT_FOLDER}/vsprojects/Debug + ${NANOPB_SRC_ROOT_FOLDER}/generator-bin + ${NANOPB_SRC_ROOT_FOLDER}/generator + NO_DEFAULT_PATH +) + +# Test protoc, try to get version +execute_process( + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --version + OUTPUT_QUIET + ERROR_QUIET + RESULT_VARIABLE ret +) +if(NOT ret EQUAL 0) + # Fallback to system protoc + unset(PROTOBUF_PROTOC_EXECUTABLE) + find_program(PROTOBUF_PROTOC_EXECUTABLE + NAMES protoc + DOC "The Google Protocol Buffers Compiler" + ) +endif() + +mark_as_advanced(PROTOBUF_PROTOC_EXECUTABLE) + +# Find nanopb generator source dir +find_path(NANOPB_GENERATOR_SOURCE_DIR + NAMES nanopb_generator.py + DOC "nanopb generator source" + PATHS + ${NANOPB_SRC_ROOT_FOLDER}/generator + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +mark_as_advanced(NANOPB_GENERATOR_SOURCE_DIR) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Nanopb DEFAULT_MSG + NANOPB_INCLUDE_DIRS + NANOPB_SRCS NANOPB_HDRS + NANOPB_GENERATOR_SOURCE_DIR + PROTOBUF_PROTOC_EXECUTABLE + ) From 5a0a868feeb1c10bb25ec9b90ac5c34a5463c8b7 Mon Sep 17 00:00:00 2001 From: MaartenS11 Date: Wed, 8 Jul 2026 14:36:13 +0200 Subject: [PATCH 03/26] Cmake changes --- CMakeLists.txt | 15 +++++++++++++++ lib/FindNanopb.cmake | 10 ++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 989035a68..1581578b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,21 @@ if (BUILD_EMULATOR) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) find_package(Threads REQUIRED) + + include(FetchContent) + FetchContent_Declare( + nanopb + GIT_REPOSITORY https://github.com/nanopb/nanopb.git + GIT_TAG 0.4.9.1 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(nanopb) + set(NANOPB_SRC_ROOT_FOLDER ${nanopb_SOURCE_DIR}) + find_program(NANOPB_GENERATOR_PLUGIN + NAMES protoc-gen-nanopb + DOC "nanopb protoc plugin" + REQUIRED + ) find_package(Nanopb REQUIRED) nanopb_generate_cpp(TARGET proto src/Debug/debug.proto) diff --git a/lib/FindNanopb.cmake b/lib/FindNanopb.cmake index 81229f188..9def7833d 100644 --- a/lib/FindNanopb.cmake +++ b/lib/FindNanopb.cmake @@ -189,10 +189,12 @@ function(NANOPB_GENERATE_CPP) set(GENERATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/nanopb/generator) set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py) - if (CMAKE_HOST_WIN32) - set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb.bat) - else() - set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb) + if(NOT NANOPB_GENERATOR_PLUGIN) + if (CMAKE_HOST_WIN32) + set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb.bat) + else() + set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb) + endif() endif() set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto) From 8698f925b60c928ccd84210183e2669487cc2386 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Thu, 9 Jul 2026 11:48:04 +0200 Subject: [PATCH 04/26] Install nanopb in GitHub actions --- .github/workflows/compile.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 1825cff8d..6cc8e98ca 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -36,6 +36,9 @@ jobs: - name: Create build folder run: mkdir build-emu + - name: Install protoc/nanopb + run: brew install nanopb + - name: Build WARDuino CLI run: cmake .. -D BUILD_EMULATOR=ON ; cmake --build . working-directory: build-emu From 1203086d3ff36a3b1b9c04b8b9f4b906338fd277 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Thu, 9 Jul 2026 12:34:21 +0200 Subject: [PATCH 05/26] Setup brew in .github action --- .github/workflows/compile.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 6cc8e98ca..c8715452e 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -36,6 +36,9 @@ jobs: - name: Create build folder run: mkdir build-emu + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@main + - name: Install protoc/nanopb run: brew install nanopb From 04a98a373b5afa8c795d7d7b89ff7c20b707e4d6 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Thu, 9 Jul 2026 13:00:45 +0200 Subject: [PATCH 06/26] Update actions --- .github/workflows/compile.yml | 1 + .github/workflows/test.yml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index c8715452e..e56735011 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -37,6 +37,7 @@ jobs: run: mkdir build-emu - name: Set up Homebrew + if: ${{ ! startsWith(matrix.os, 'macos') }} uses: Homebrew/actions/setup-homebrew@main - name: Install protoc/nanopb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8994a650b..f1cd0a388 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,6 +44,12 @@ jobs: with: node-version: 20 + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@main + + - name: Install protoc/nanopb + run: brew install nanopb + - name: Build warduino cli run: | cmake . -D BUILD_EMULATOR=ON @@ -99,6 +105,12 @@ jobs: with: node-version: 20 + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@main + + - name: Install protoc/nanopb + run: brew install nanopb + - name: Build warduino cli run: | cmake . -D BUILD_EMULATOR=ON @@ -152,6 +164,12 @@ jobs: with: node-version: 20 + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@main + + - name: Install protoc/nanopb + run: brew install nanopb + - name: Build warduino cli run: | cmake . -D BUILD_EMULATOR=ON From cbc38be2e4b2b57b39299037a28f696ea1da249e Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Tue, 18 Aug 2026 12:46:18 +0200 Subject: [PATCH 07/26] Tweak proto --- src/Debug/debug.proto | 96 +++++++++++++++++++++++++------------------ src/Debug/debugger.h | 3 ++ 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/Debug/debug.proto b/src/Debug/debug.proto index a9cc1cb8b..d89fbafb3 100644 --- a/src/Debug/debug.proto +++ b/src/Debug/debug.proto @@ -9,45 +9,6 @@ option cc_enable_arenas = false; //// -enum State { - WARDUINOrun = 0; - WARDUINOpause = 1; - WARDUINOstep = 2; - PROXYrun = 3; - PROXYhalt = 4; -} - -enum Command { - // standard debug operations - run = 0; - halt = 1; - pause = 2; - step = 3; - bpadd = 4; - bprem = 5; - - // inspection operations - dump = 6; - dumplocals = 7; - snapshot = 8; - dumpevents = 9; - dumpcallbacks = 10; - - // update operations - updatefunc = 11; - updatelocal = 12; - updatecallbacks = 13; - - // edward debug operations - loadsnapshot = 14; - proxify = 15; - proxyadd = 16; - proxyrem = 17; - proxycall = 18; - popevent = 19; - pushevent = 20; -} - //// 2 types of messages: DebugMessage or Notification: message DebugMessage { // messages FROM frontend @@ -59,7 +20,7 @@ message DebugMessage { // messages FROM frontend message Notification { // messages TO frontend enum Type { - // simple notifications: + // simple notifications: TODO a single acknowledgement continued = 0; halted = 1; paused = 2; @@ -84,6 +45,61 @@ message Notification { // messages TO frontend optional Payload payload = 2; } + +// DebugMessage contents + +enum State { + WARDUINOrun = 0; + WARDUINOpause = 1; + WARDUINOstep = 2; + PROXYrun = 3; // Running state used when executing a proxy call. During + // this state the call is set up and executed by the main + // loop. After execution, the state is restored to + // PROXYhalt + PROXYhalt = 4; // Do not run the program (program runs on computer, which + // sends messages for primitives, do forward interrupts) +} + +enum Command { + // standard debug operations + run = 0; + halt = 1; + pause = 2; + step = 3; + stepover = 4; + bpadd = 5; + bprem = 6; + + // inspection operations + dump = 7; + dumplocals = 8; + snapshot = 9; + dumpevents = 10; + dumpcallbacks = 11; + + // update operations + updatefunc = 12; + updatelocal = 13; + updatecallbacks = 14; + + // edward debug operations + loadsnapshot = 15; + proxify = 16; + proxyadd = 17; + proxyrem = 18; + proxycall = 19; + popevent = 20; + pushevent = 21; + + // new instructions + continuefor = 22; + inspect = 23; + reset = 24; + invoke = 25; + + // todo overrides +} + //// content of messages: message Payload { diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 3a5b66268..193ee65d1 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -16,6 +16,9 @@ #include "../Threading/warduino-thread.h" #include "../Utils/sockets.h" +#include +#include "debug.pb.h" + struct Module; struct Block; struct StackValue; From 9f78e55d62d40b04ec77a68291a210fb3e50e746 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 19 Aug 2026 12:29:40 +0200 Subject: [PATCH 08/26] Improve `.proto` for small message size --- src/Debug/debug.proto | 288 +++++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/src/Debug/debug.proto b/src/Debug/debug.proto index d89fbafb3..5186b364f 100644 --- a/src/Debug/debug.proto +++ b/src/Debug/debug.proto @@ -1,4 +1,15 @@ -//// WARDUINO DEBUG API +// WARDuino debug protocol. +// +// Command and NotificationType are transport-level discriminators. They are +// written as a single byte before the protobuf payload; they are not encoded +// as fields inside another protobuf message. +// +// If the transport does not already preserve packet boundaries, frame packets +// as: +// +// [type: uint8][payload length: varint][protobuf payload] +// +// Empty commands and notifications have a zero-length protobuf payload. syntax = "proto3"; @@ -7,192 +18,181 @@ package debug; option optimize_for = LITE_RUNTIME; option cc_enable_arenas = false; -//// - -//// 2 types of messages: DebugMessage or Notification: +// Frontend -> WARDuino. +// The receiver selects the payload schema from the command byte. +enum Command { + COMMAND_RUN = 0; // no payload + COMMAND_HALT = 1; // no payload + COMMAND_PAUSE = 2; // no payload + COMMAND_STEP = 3; // no payload + COMMAND_STEP_OVER = 4; // no payload + COMMAND_ADD_BREAKPOINT = 5; // Breakpoint + COMMAND_REMOVE_BREAKPOINT = 6; // Breakpoint + + COMMAND_DUMP = 7; // no payload + COMMAND_DUMP_LOCALS = 8; // no payload + COMMAND_SNAPSHOT = 9; // no payload + COMMAND_DUMP_EVENTS = 10; // Range + COMMAND_DUMP_CALLBACKS = 11; // no payload + + COMMAND_UPDATE_FUNCTION = 12; // Function + COMMAND_UPDATE_LOCAL = 13; // LocalUpdate + COMMAND_UPDATE_CALLBACKS = 14; // CallbackMapping + + COMMAND_LOAD_SNAPSHOT = 15; // Snapshot + COMMAND_PROXIFY = 16; // no payload + COMMAND_ADD_PROXY = 17; // FunctionRef + COMMAND_REMOVE_PROXY = 18; // FunctionRef + COMMAND_PROXY_CALL = 19; // RemoteFunctionCall + COMMAND_POP_EVENT = 20; // no payload + COMMAND_PUSH_EVENT = 21; // Event + + COMMAND_CONTINUE_FOR = 22; // ContinueFor + COMMAND_INSPECT = 23; // Inspect + COMMAND_RESET = 24; // no payload + COMMAND_INVOKE = 25; // RemoteFunctionCall +} + +// WARDuino -> frontend. +// The receiver selects the payload schema from the notification byte. +enum NotificationType { + NOTIFICATION_CONTINUED = 0; // no payload + NOTIFICATION_HALTED = 1; // no payload + NOTIFICATION_PAUSED = 2; // no payload + NOTIFICATION_STEPPED = 3; // no payload + NOTIFICATION_HIT_BREAKPOINT = 4; // HitBreakpoint + NOTIFICATION_NEW_EVENT = 5; // NewEvent (zero-length payload) + + NOTIFICATION_FUNCTION_DUMP = 6; // Function + NOTIFICATION_LOCALS_DUMP = 7; // Locals + NOTIFICATION_SNAPSHOT = 8; // Snapshot + NOTIFICATION_EVENTS_DUMP = 9; // EventsQueue + NOTIFICATION_CALLBACKS_DUMP = 10; // CallbackMapping + NOTIFICATION_CHANGE_AFFECTED = 11; // no payload + + NOTIFICATION_MALFORMED = 12; // no payload + NOTIFICATION_UNKNOWN_COMMAND = 13; // no payload +} -message DebugMessage { // messages FROM frontend - Command command = 1; +enum State { + STATE_WARDUINO_RUN = 0; + STATE_WARDUINO_PAUSE = 1; + STATE_WARDUINO_STEP = 2; + STATE_PROXY_RUN = 3; + STATE_PROXY_HALT = 4; +} - // optional payload - optional Payload payload = 2; +// A virtual program address. +message CodeLocation { + uint32 module_index = 1; + uint32 program_counter = 2; } -message Notification { // messages TO frontend - enum Type { - // simple notifications: TODO a single acknowledgement - continued = 0; - halted = 1; - paused = 2; - stepped = 3; - hitbreakpoint = 4; - newevent = 5; // a new event got pushed from proxy - changeaffected = 11; +message Breakpoint { + CodeLocation location = 1; +} - // payload responses: - dump = 6; - dumplocals = 7; - snapshot = 8; - dumpevents = 9; - dumpcallbacks = 10; +message HitBreakpoint { + CodeLocation location = 1; +} - // errors: - malformed = 12; // received message was malformed - unknown = 13; // received unknown command - } +// The notification type carries all information for this event. The empty +// message exists for host-side reflection, but no protobuf bytes are sent. +message NewEvent {} - Type type = 1; - optional Payload payload = 2; +message ContinueFor { + uint32 count = 1; } +// Execution-state selectors understood by the VM. Keeping them as bytes lets +// the protocol add selectors without changing this schema. +message Inspect { + bytes state = 1; +} -// DebugMessage contents - -enum State { - WARDUINOrun = 0; - WARDUINOpause = 1; - WARDUINOstep = 2; - PROXYrun = 3; // Running state used when executing a proxy call. During - // this state the call is set up and executed by the main - // loop. After execution, the state is restored to - // PROXYhalt - PROXYhalt = 4; // Do not run the program (program runs on computer, which - // sends messages for primitives, do forward interrupts) +message FunctionRef { + uint32 function_index = 1; } -enum Command { - // standard debug operations - run = 0; - halt = 1; - pause = 2; - step = 3; - stepover = 4; - bpadd = 5; - bprem = 6; - - // inspection operations - dump = 7; - dumplocals = 8; - snapshot = 9; - dumpevents = 10; - dumpcallbacks = 11; - - // update operations - updatefunc = 12; - updatelocal = 13; - updatecallbacks = 14; - - // edward debug operations - loadsnapshot = 15; - proxify = 16; - proxyadd = 17; - proxyrem = 18; - proxycall = 19; - popevent = 20; - pushevent = 21; - - // new instructions - continuefor = 22; - inspect = 23; - reset = 24; - invoke = 25; - - // todo overrides -} - -//// content of messages: - -message Payload { - oneof payload { - Snapshot snapshot = 1; // dumps - Locals locals = 2; // dumplocals - Function function = 3; // dumps, updatefunc, proxyadd/rem - uint32 breakpoint = 4; // bpadd, bprem - EventsQueue queue = 5; // dumps - Event event = 6; // event operations - Range range = 7; // dump eventsqueue - CallbackMapping callbacks = 8; // dump/update callbacks - RFC call = 9; // proxycall - } +message LocalUpdate { + uint32 index = 1; + Value value = 2; } message Snapshot { - // minimal state fields - uint32 program_counter = 1; - State state = 2; - repeated uint32 breakpoints = 3; - repeated Function functions = 4; - repeated CallstackEntry callstack = 5; + uint32 program_counter = 1; + State state = 2; + repeated uint32 breakpoints = 3; + repeated Function functions = 4; + repeated CallstackEntry callstack = 5; - // optional extending state fields - optional Locals locals = 6; - optional EventsQueue queue = 7; - optional CallbackMapping callbacks = 8; + Locals locals = 6; + EventsQueue queue = 7; + CallbackMapping callbacks = 8; } message Function { - int32 fidx = 1; - - // optional fields for updating: - Range range = 2; // from, to - Locals locals = 3; - bytes instructions = 4; // implementation + uint32 function_index = 1; + Range range = 2; + Locals locals = 3; + bytes instructions = 4; } -message RFC { // Remote Function Call - int32 fidx = 1; - int32 argcount = 2; - repeated Value args = 3; +message RemoteFunctionCall { + uint32 function_index = 1; + repeated Value arguments = 2; } message CallstackEntry { - int32 type = 1; - int32 fidx = 2; - int32 sp = 3; - int32 fp = 4; - int32 start = 5; - int32 ra = 6; + uint32 type = 1; + uint32 function_index = 2; + uint32 stack_pointer = 3; + uint32 frame_pointer = 4; + uint32 start = 5; + uint32 return_address = 6; } message Locals { - int32 count = 1; - repeated Value values = 2; + repeated Value values = 1; } +// The oneof tag is the value type, so a separate type enum and a decimal +// string are unnecessary. Fixed-width fields preserve WebAssembly bits and +// are fast to construct on the MCU. message Value { - enum Type { - i32 = 0; - i64 = 1; - f32 = 2; - f64 = 3; - any = 4; - } - Type type = 1; - string value = 2; // can be any wasm value - int32 index = 3; + oneof data { + fixed32 i32_bits = 1; + fixed64 i64_bits = 2; + fixed32 f32_bits = 3; + fixed64 f64_bits = 4; + bytes raw = 5; + } + + uint32 index = 6; } message CallbackMapping { - repeated CallbackEntry entries = 1; + repeated CallbackEntry entries = 1; } message CallbackEntry { - string topic = 1; - repeated int32 tidx = 2; + string topic = 1; + repeated uint32 table_indexes = 2; } message EventsQueue { - int32 count = 1; // total count of events in queue - repeated Event events = 2; // slice of events - optional Range range = 3; // range of returned slice + // Total events in the queue; this can exceed the returned slice length. + uint32 total_count = 1; + repeated Event events = 2; + Range range = 3; } message Event { - string topic = 1; - string payload = 2; + string topic = 1; + bytes payload = 2; } message Range { - int32 start = 1; // start index - int32 end = 2; // end index + uint32 start = 1; + uint32 end = 2; } From bd765fa199d66ecf131af78681e8becf2555bf03 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 19 Aug 2026 12:47:54 +0200 Subject: [PATCH 09/26] Improve docs --- .gitignore | 4 ++++ src/Debug/debug.proto | 11 +++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0e6a68346..f6cfe209a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ .idea/ +.air/ .vscode/ .ccls +.cache + *.bin *.ipch *.o @@ -35,3 +38,4 @@ core venv *.wasm + diff --git a/src/Debug/debug.proto b/src/Debug/debug.proto index 5186b364f..de3d47d71 100644 --- a/src/Debug/debug.proto +++ b/src/Debug/debug.proto @@ -4,8 +4,7 @@ // written as a single byte before the protobuf payload; they are not encoded // as fields inside another protobuf message. // -// If the transport does not already preserve packet boundaries, frame packets -// as: +// Frame packet structure: // // [type: uint8][payload length: varint][protobuf payload] // @@ -84,16 +83,16 @@ enum State { // A virtual program address. message CodeLocation { - uint32 module_index = 1; - uint32 program_counter = 2; + uint32 module_index = 1; + uint32 program_counter = 2; } message Breakpoint { - CodeLocation location = 1; + CodeLocation location = 1; } message HitBreakpoint { - CodeLocation location = 1; + CodeLocation location = 1; } // The notification type carries all information for this event. The empty From 6eafb430012888295cd4afc19eddb349256d15f8 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 19 Aug 2026 13:34:28 +0200 Subject: [PATCH 10/26] Add missing dbg commands to proto --- src/Debug/debug.proto | 64 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Debug/debug.proto b/src/Debug/debug.proto index de3d47d71..bc69bc5f1 100644 --- a/src/Debug/debug.proto +++ b/src/Debug/debug.proto @@ -35,8 +35,11 @@ enum Command { COMMAND_DUMP_CALLBACKS = 11; // no payload COMMAND_UPDATE_FUNCTION = 12; // Function - COMMAND_UPDATE_LOCAL = 13; // LocalUpdate + COMMAND_UPDATE_LOCAL = 13; // ValueUpdate COMMAND_UPDATE_CALLBACKS = 14; // CallbackMapping + COMMAND_UPDATE_MODULE = 26; // ModuleUpdate + COMMAND_UPDATE_GLOBAL = 27; // ValueUpdate + COMMAND_UPDATE_STACK = 28; // ValueUpdate COMMAND_LOAD_SNAPSHOT = 15; // Snapshot COMMAND_PROXIFY = 16; // no payload @@ -50,6 +53,11 @@ enum Command { COMMAND_INSPECT = 23; // Inspect COMMAND_RESET = 24; // no payload COMMAND_INVOKE = 25; // RemoteFunctionCall + + + COMMAND_SET_SNAPSHOT_POLICY = 29; // SnapshotPolicyConfig + COMMAND_SET_OVERRIDE = 30; // Override + COMMAND_REMOVE_OVERRIDE = 31; // Override } // WARDuino -> frontend. @@ -71,6 +79,9 @@ enum NotificationType { NOTIFICATION_MALFORMED = 12; // no payload NOTIFICATION_UNKNOWN_COMMAND = 13; // no payload + NOTIFICATION_OPERATION_RESULT = 14; // OperationResult + NOTIFICATION_REMOTE_FUNCTION_RESULT = 15; // RemoteFunctionResult + NOTIFICATION_CHECKPOINT = 16; // Checkpoint } enum State { @@ -113,7 +124,7 @@ message FunctionRef { uint32 function_index = 1; } -message LocalUpdate { +message ValueUpdate { uint32 index = 1; Value value = 2; } @@ -128,6 +139,15 @@ message Snapshot { Locals locals = 6; EventsQueue queue = 7; CallbackMapping callbacks = 8; + + repeated Value globals = 9; + repeated Value stack = 10; + TableState table = 11; + MemoryState memory = 12; + repeated uint32 branch_table = 13; + repeated IOState io = 14; + repeated Override overrides = 15; + uint32 heap_used = 16; } message Function { @@ -195,3 +215,43 @@ message Range { uint32 start = 1; uint32 end = 2; } + +message ModuleUpdate { bytes wasm = 1; } +message IndexedValues { repeated Value values = 1; } + +enum SnapshotPolicy { + SNAPSHOT_POLICY_NONE = 0; + SNAPSHOT_POLICY_EVERY_INSTRUCTION = 1; + SNAPSHOT_POLICY_CHECKPOINTING = 2; +} + +message SnapshotPolicyConfig { + SnapshotPolicy policy = 1; + uint32 interval = 2; + uint32 minimum_return_count = 3; + bytes selected_state = 4; +} + +message Override { + string primitive_name = 1; + repeated fixed32 argument_words = 2; + fixed32 result = 3; +} + +message OperationResult { Command command = 1; bool success = 2; } +message RemoteFunctionResult { + bool success = 1; + repeated Value results = 2; + bytes error = 3; +} +message Checkpoint { + uint32 instruction_count = 1; + bool has_primitive_call = 2; + uint32 primitive_function_index = 3; + repeated Value arguments = 4; + repeated Value results = 5; + Snapshot snapshot = 6; +} +message TableState { uint32 initial = 1; uint32 maximum = 2; repeated uint32 entries = 3; } +message MemoryState { uint32 initial = 1; uint32 maximum = 2; uint32 pages = 3; bytes bytes = 4; } +message IOState { string key = 1; bool output = 2; sint32 value = 3; } From a56e7688193cccdad7999f86f5c1c2ede549b001 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 19 Aug 2026 14:18:37 +0200 Subject: [PATCH 11/26] Refactor messaging to protobuf --- CMakeLists.txt | 25 +- src/Debug/debugger.cpp | 1268 +++++++++++++----------------- src/Debug/debugger.h | 39 +- src/Edward/proxy.cpp | 27 +- src/Edward/proxy.h | 2 +- src/Edward/proxy_supervisor.cpp | 2 +- src/Utils/sockets.cpp | 37 + src/Utils/sockets.h | 8 + src/WARDuino/CallbackHandler.cpp | 7 +- 9 files changed, 617 insertions(+), 798 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1581578b8..360b33bff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,13 +36,9 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") set(WARDUINO_VERSION_STRING "${PROJECT_VERSION}") configure_file(src/config.h.in include/warduino/config.h) -# Build the emulator version of WARDuino -if (BUILD_EMULATOR) - set(EXTERNAL_LIB_HEADERS lib/json/single_include) - set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) - - find_package(Threads REQUIRED) - +# Both host targets use one generated nanopb schema target. +if (BUILD_EMULATOR OR BUILD_UNITTEST) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) include(FetchContent) FetchContent_Declare( nanopb @@ -52,14 +48,15 @@ if (BUILD_EMULATOR) ) FetchContent_MakeAvailable(nanopb) set(NANOPB_SRC_ROOT_FOLDER ${nanopb_SOURCE_DIR}) - find_program(NANOPB_GENERATOR_PLUGIN - NAMES protoc-gen-nanopb - DOC "nanopb protoc plugin" - REQUIRED - ) + find_program(NANOPB_GENERATOR_PLUGIN NAMES protoc-gen-nanopb REQUIRED) find_package(Nanopb REQUIRED) - nanopb_generate_cpp(TARGET proto src/Debug/debug.proto) +endif () + +# Build the emulator version of WARDuino +if (BUILD_EMULATOR) + set(EXTERNAL_LIB_HEADERS lib/json/single_include) + find_package(Threads REQUIRED) set(SOURCE_FILES src/WARDuino/WARDuino.cpp @@ -141,7 +138,7 @@ if (BUILD_UNITTEST) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) message(DEBUG "Add executable for " ${TEST_FILE}) add_executable(${TEST_NAME} ${TEST_FILE} ${SOURCE_FILES} ${SHARED_SRC}) - target_link_libraries(${TEST_NAME} PRIVATE doctest::doctest) + target_link_libraries(${TEST_NAME} PRIVATE doctest::doctest proto) target_include_directories(${TEST_NAME} PRIVATE ${EXTERNAL_LIB_HEADERS} "${PROJECT_BINARY_DIR}/include") add_test(${TEST_NAME} ${TEST_NAME}) endforeach () diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 0cb65b9b3..222ffd400 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -4,11 +4,6 @@ #include #include #include -#ifndef ARDUINO -#include -#else -#include "../../lib/json/single_include/nlohmann/json.hpp" -#endif #include "../Memory/mem.h" #include "../Utils//util.h" @@ -39,101 +34,136 @@ void Debugger::setChannel(Channel *duplex) { this->channel = duplex; } -void Debugger::addDebugMessage(size_t len, const uint8_t *buff) { - this->parseDebugBuffer(len, buff); - uint8_t *data{}; - while (!this->parsedInterrupts.empty()) { - data = this->parsedInterrupts.front(); - this->parsedInterrupts.pop(); - if (*data == interruptRecvCallbackmapping) { - size_t startIdx = 0; - while (buff[startIdx] != '7' || buff[startIdx + 1] != '5' || - buff[startIdx + 2] != '{') { - startIdx++; - } - size_t endIdx = startIdx; - while (buff[endIdx] != '\n') { - endIdx++; - } - auto *msg = static_cast(acalloc( - sizeof(uint8_t), (endIdx - startIdx), "interrupt buffer")); - memcpy(msg, buff + startIdx, (endIdx - startIdx) * sizeof(uint8_t)); - *msg = *data; - free(data); - this->pushMessage(msg); - } else { - this->pushMessage(data); +namespace { + +bool decodeFrameLength(const std::vector &bytes, size_t *headerSize, + size_t *payloadSize) { + if (bytes.size() < 2) return false; + uint32_t value = 0; + for (size_t i = 0; i < 5; ++i) { + const size_t offset = i + 1; + if (offset >= bytes.size()) return false; + const uint8_t byte = bytes[offset]; + if (i == 4 && (byte & 0xf0U) != 0) { *headerSize = SIZE_MAX; return false; } + value |= static_cast(byte & 0x7fU) << (i * 7U); + if ((byte & 0x80U) == 0) { + if (i > 0 && value < (1U << (i * 7U))) { *headerSize = SIZE_MAX; return false; } + *headerSize = offset + 1; + *payloadSize = value; + return true; } } + return false; } -void Debugger::pushMessage(uint8_t *msg) { - warduino::lock_guard const lg(messageQueueMutex); - this->debugMessages.push_back(msg); - this->freshMessages = !this->debugMessages.empty(); - this->messageQueueConditionVariable.notify_one(); +bool isKnownCommand(const uint8_t type) { + return type <= static_cast(debug_Command_COMMAND_REMOVE_OVERRIDE); } -void Debugger::parseDebugBuffer(size_t len, const uint8_t *buff) { - for (size_t i = 0; i < len; i++) { - bool success = true; - int r = 0; +template +bool decodePayload(const std::vector &payload, const pb_msgdesc_t *fields, + T *message) { + pb_istream_t stream = pb_istream_from_buffer(payload.data(), payload.size()); + return pb_decode(&stream, fields, message); +} - // TODO replace by real binary - switch (buff[i]) { - case '0' ... '9': - r = buff[i] - '0'; - break; - case 'A' ... 'F': - r = buff[i] - 'A' + 10; - break; - case 'a' ... 'f': - r = buff[i] - 'a' + 10; - break; - default: - success = false; +} // namespace + +void Debugger::addDebugMessage(const size_t len, const uint8_t *buff) { + if (len == 0 || buff == nullptr) return; + parseDebugBuffer(len, buff); +} + +void Debugger::pushMessage(DebugMessage msg) { + warduino::lock_guard const lg(messageQueueMutex); + debugMessages.emplace_back(std::move(msg)); + freshMessages = !debugMessages.empty(); + messageQueueConditionVariable.notify_one(); +} + +void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { + pendingFrameBytes.insert(pendingFrameBytes.end(), buff, buff + len); + while (!pendingFrameBytes.empty()) { + if (!isKnownCommand(pendingFrameBytes.front())) { + pendingFrameBytes.clear(); + sendNotification(debug_NotificationType_NOTIFICATION_UNKNOWN_COMMAND); + continue; } - if (!success) { - if (this->interruptEven) { - if (!this->interruptBuffer.empty()) { - // done, send to process - // TODO: pointer gets leaked! - auto data = static_cast( - acalloc(sizeof(uint8_t), this->interruptBuffer.size(), - "interrupt buffer")); - memcpy(data, this->interruptBuffer.data(), - this->interruptBuffer.size() * sizeof(uint8_t)); - this->parsedInterrupts.push(data); - this->interruptBuffer.clear(); - } - } else { - this->interruptBuffer.clear(); - this->interruptEven = true; - dbg_warn("Dropped interrupt: could not process"); + size_t headerSize = 0; + size_t payloadSize = 0; + const bool completeLength = decodeFrameLength(pendingFrameBytes, &headerSize, + &payloadSize); + if (!completeLength) { + if (headerSize == SIZE_MAX || pendingFrameBytes.size() >= 6) { + pendingFrameBytes.clear(); + sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); } - } else { // good parse - if (!this->interruptEven) { - this->interruptLastChar = - (this->interruptLastChar << 4u) + static_cast(r); - this->interruptBuffer.push_back(this->interruptLastChar); - } else { - this->interruptLastChar = static_cast(r); - } - this->interruptEven = !this->interruptEven; + return; + } + if (payloadSize > maxFramePayload) { + pendingFrameBytes.clear(); + sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + return; } + if (pendingFrameBytes.size() < headerSize + payloadSize) return; + + DebugMessage message{static_cast(pendingFrameBytes[0]), {}}; + message.payload.assign(pendingFrameBytes.begin() + static_cast(headerSize), + pendingFrameBytes.begin() + static_cast(headerSize + payloadSize)); + pendingFrameBytes.erase(pendingFrameBytes.begin(), + pendingFrameBytes.begin() + static_cast(headerSize + payloadSize)); + pushMessage(std::move(message)); } } -uint8_t *Debugger::getDebugMessage() { +std::optional Debugger::getDebugMessage() { warduino::lock_guard const lg(messageQueueMutex); - uint8_t *ret = nullptr; - if (!this->debugMessages.empty()) { - ret = this->debugMessages.front(); - this->debugMessages.pop_front(); + if (debugMessages.empty()) { + freshMessages = false; + return std::nullopt; } - this->freshMessages = !this->debugMessages.empty(); - return ret; + DebugMessage message = std::move(debugMessages.front()); + debugMessages.pop_front(); + freshMessages = !debugMessages.empty(); + return message; +} + +bool Debugger::sendNotification(const debug_NotificationType type, + const pb_msgdesc_t *fields, + const void *payload) const { + if (channel == nullptr) return false; + size_t payloadSize = 0; + if (fields != nullptr && payload != nullptr && + !pb_get_encoded_size(&payloadSize, fields, payload)) { + return false; + } + std::vector frame; + frame.reserve(1 + 5 + payloadSize); + frame.push_back(static_cast(type)); + size_t length = payloadSize; + do { + uint8_t byte = static_cast(length & 0x7fU); + length >>= 7U; + if (length != 0) byte |= 0x80U; + frame.push_back(byte); + } while (length != 0); + if (payloadSize != 0) { + const size_t offset = frame.size(); + frame.resize(offset + payloadSize); + pb_ostream_t stream = pb_ostream_from_buffer(frame.data() + offset, payloadSize); + if (!pb_encode(&stream, fields, payload)) return false; + } + return channel->writeBytes(frame.data(), frame.size()) == + static_cast(frame.size()); +} + +void Debugger::sendOperationResult(const debug_Command command, const bool success) const { + debug_OperationResult result = debug_OperationResult_init_zero; + result.command = command; + result.success = success; + sendNotification(debug_NotificationType_NOTIFICATION_OPERATION_RESULT, + debug_OperationResult_fields, &result); } void Debugger::addBreakpoint(uint8_t *loc) { this->breakpoints.insert(loc); } @@ -147,12 +177,14 @@ bool Debugger::isBreakpoint(uint8_t *loc) { } void Debugger::notifyBreakpoint(Module *m, uint8_t *pc_ptr) { - if (snapshotPolicy == SnapshotPolicy::checkpointing) { - checkpoint(m); - } - this->mark = nullptr; - const uint32_t bp = toVirtualAddress(pc_ptr, m); - this->channel->write("AT %" PRIu32 "!\n", bp); + if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m); + mark = nullptr; + debug_HitBreakpoint hit = debug_HitBreakpoint_init_zero; + hit.has_location = true; + hit.location.module_index = 0; + hit.location.program_counter = toVirtualAddress(pc_ptr, m); + sendNotification(debug_NotificationType_NOTIFICATION_HIT_BREAKPOINT, + debug_HitBreakpoint_fields, &hit); } /** @@ -176,239 +208,370 @@ void Debugger::notifyBreakpoint(Module *m, uint8_t *pc_ptr) { * - `0x20` : Replace the content body of a function by a new function given * as payload (immediately following `0x10`), see #readChange */ -bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { - uint8_t *interruptData = this->getDebugMessage(); - if (interruptData == nullptr) { - fflush(stdout); - return false; +namespace { + +bool collectBytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { + auto *out = static_cast *>(*arg); + out->resize(stream->bytes_left); + return out->empty() || pb_read(stream, out->data(), out->size()); +} + +[[maybe_unused]] bool collectWords(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { + auto *out = static_cast *>(*arg); + while (stream->bytes_left != 0) { + uint32_t value = 0; + if (!pb_decode_fixed32(stream, &value)) return false; + out->push_back(value); } - debug("received interrupt %x\n", *interruptData); - fflush(stdout); + return true; +} - this->channel->write("Interrupt: %x\n", *interruptData); +void setDecodeCallback(pb_callback_t *callback, std::vector *out) { + callback->funcs.decode = collectBytes; + callback->arg = out; +} + +bool collectVarints(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { + auto *out = static_cast *>(*arg); + while (stream->bytes_left != 0) { + uint64_t value = 0; + if (!pb_decode_varint(stream, &value) || value > UINT32_MAX) return false; + out->push_back(static_cast(value)); + } + return true; +} + +struct DecodedCallbackEntry { std::string topic; std::vector indexes; }; +bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { + auto *entries = static_cast *>(*arg); + debug_CallbackEntry entry = debug_CallbackEntry_init_zero; + std::vector topic; + std::vector indexes; + setDecodeCallback(&entry.topic, &topic); + entry.table_indexes.funcs.decode = collectVarints; + entry.table_indexes.arg = &indexes; + if (!pb_decode(stream, debug_CallbackEntry_fields, &entry)) return false; + entries->push_back({std::string(topic.begin(), topic.end()), std::move(indexes)}); + return true; +} + +std::optional findImportedFunction(Module *m, const std::string &name) { + for (uint32_t index = 0; index < m->import_count; ++index) { + if (m->functions[index].import_field != nullptr && + name == m->functions[index].import_field) return index; + } + return std::nullopt; +} + +bool collectValues(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { + auto *out = static_cast *>(*arg); + debug_Value value = debug_Value_init_zero; + if (!pb_decode(stream, debug_Value_fields, &value)) return false; + out->push_back(value); + return true; +} + +bool valueFromProto(const debug_Value &from, StackValue *to) { + switch (from.which_data) { + case debug_Value_i32_bits_tag: to->value_type = I32; to->value.uint32 = from.data.i32_bits; return true; + case debug_Value_i64_bits_tag: to->value_type = I64; to->value.uint64 = from.data.i64_bits; return true; + case debug_Value_f32_bits_tag: to->value_type = F32; to->value.uint32 = from.data.f32_bits; return true; + case debug_Value_f64_bits_tag: to->value_type = F64; to->value.uint64 = from.data.f64_bits; return true; + default: return false; + } +} - long start = 0, size = 0; - switch (*interruptData) { - case interruptRUN: - this->handleInterruptRUN(m, program_state); - free(interruptData); +[[maybe_unused]] void valueToProto(const StackValue &from, const uint32_t index, debug_Value *to) { + *to = debug_Value_init_zero; + to->index = index; + switch (from.value_type) { + case I32: to->which_data = debug_Value_i32_bits_tag; to->data.i32_bits = from.value.uint32; break; + case I64: to->which_data = debug_Value_i64_bits_tag; to->data.i64_bits = from.value.uint64; break; + case F32: to->which_data = debug_Value_f32_bits_tag; to->data.f32_bits = from.value.uint32; break; + case F64: to->which_data = debug_Value_f64_bits_tag; to->data.f64_bits = from.value.uint64; break; + default: break; + } +} + +} // namespace + +bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { + std::optional message = getDebugMessage(); + if (!message) return false; + + const auto malformed = [this]() { + sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + }; + const auto requireEmpty = [&message, &malformed]() { + if (!message->payload.empty()) { malformed(); return false; } + return true; + }; + + switch (message->type) { + case debug_Command_COMMAND_RUN: + if (!requireEmpty()) break; + handleInterruptRUN(m, program_state); + sendNotification(debug_NotificationType_NOTIFICATION_CONTINUED); break; - case interruptHALT: - this->channel->write("STOP!\n"); - this->channel->close(); - free(interruptData); - delete m->warduino; - exit(0); - case interruptPAUSE: - this->pauseRuntime(m); - // Make a checkpoint so the debugger knows the current state and - // knows how many instructions were executed since the last - // checkpoint. - if (snapshotPolicy == SnapshotPolicy::checkpointing) { - checkpoint(m, true); - } - this->channel->write("PAUSE!\n"); - free(interruptData); + case debug_Command_COMMAND_HALT: + if (!requireEmpty()) break; + sendNotification(debug_NotificationType_NOTIFICATION_HALTED); + if (channel != nullptr) channel->close(); break; - case interruptSTEP: - this->handleSTEP(m, program_state); - free(interruptData); + case debug_Command_COMMAND_PAUSE: + if (!requireEmpty()) break; + pauseRuntime(m); + if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); + sendNotification(debug_NotificationType_NOTIFICATION_PAUSED); break; - case interruptSTEPOver: - this->handleSTEPOver(m, program_state); - free(interruptData); + case debug_Command_COMMAND_STEP: + if (!requireEmpty()) break; + handleSTEP(m, program_state); break; - case interruptBPAdd: // Breakpoint - case interruptBPRem: // Breakpoint remove - this->handleInterruptBP(m, interruptData); - free(interruptData); + case debug_Command_COMMAND_STEP_OVER: + if (!requireEmpty()) break; + handleSTEPOver(m, program_state); break; - case interruptContinueFor: { - uint8_t *data = interruptData + 1; - uint32_t amount = read_B32(&data); - debug("Continue for %" PRIu32 " instruction(s)\n", amount); - remaining_instructions = (int32_t)amount; + case debug_Command_COMMAND_ADD_BREAKPOINT: + case debug_Command_COMMAND_REMOVE_BREAKPOINT: { + debug_Breakpoint breakpoint = debug_Breakpoint_init_zero; + if (!decodePayload(message->payload, debug_Breakpoint_fields, &breakpoint) || + !breakpoint.has_location || breakpoint.location.module_index != 0 || + !isToPhysicalAddrPossible(breakpoint.location.program_counter, m)) { + malformed(); + break; + } + uint8_t *address = toPhysicalAddress(breakpoint.location.program_counter, m); + if (message->type == debug_Command_COMMAND_ADD_BREAKPOINT) addBreakpoint(address); + else deleteBreakpoint(address); + sendOperationResult(message->type, true); + break; + } + case debug_Command_COMMAND_CONTINUE_FOR: { + debug_ContinueFor request = debug_ContinueFor_init_zero; + if (!decodePayload(message->payload, debug_ContinueFor_fields, &request) || request.count == 0) { + malformed(); + break; + } + remaining_instructions = static_cast(request.count); *program_state = WARDUINOrun; - free(interruptData); + sendNotification(debug_NotificationType_NOTIFICATION_CONTINUED); break; } - case interruptDUMP: - this->pauseRuntime(m); - this->dump(m); - free(interruptData); + case debug_Command_COMMAND_DUMP: + if (!requireEmpty()) break; + pauseRuntime(m); + snapshot(m); break; - case interruptDUMPLocals: - this->pauseRuntime(m); - this->dumpLocals(m); - this->channel->write("\n"); - free(interruptData); + case debug_Command_COMMAND_DUMP_LOCALS: + if (!requireEmpty()) break; + pauseRuntime(m); + dumpLocals(m); break; - case interruptDUMPFull: - this->pauseRuntime(m); - this->dump(m, true); - free(interruptData); + case debug_Command_COMMAND_SNAPSHOT: + if (!requireEmpty()) break; + pauseRuntime(m); + snapshot(m); break; - case interruptReset: - this->reset(m); - free(interruptData); + case debug_Command_COMMAND_DUMP_EVENTS: { + debug_Range range = debug_Range_init_zero; + if (!decodePayload(message->payload, debug_Range_fields, &range) || range.end < range.start) { malformed(); break; } + dumpEvents(range.start, range.end - range.start); break; - case interruptUPDATEFun: - this->channel->write("CHANGE function!\n"); - Debugger::handleChangedFunction(m, interruptData); - // do not free(interruptData); - // we need it to run that code - // TODO: free double replacements + } + case debug_Command_COMMAND_DUMP_CALLBACKS: + if (!requireEmpty()) break; + dumpCallbackmapping(); break; - case interruptUPDATELocal: - this->channel->write("CHANGE local!\n"); - this->handleChangedLocal(m, interruptData); - free(interruptData); + case debug_Command_COMMAND_UPDATE_LOCAL: + case debug_Command_COMMAND_UPDATE_GLOBAL: + case debug_Command_COMMAND_UPDATE_STACK: { + debug_ValueUpdate update = debug_ValueUpdate_init_zero; + if (!decodePayload(message->payload, debug_ValueUpdate_fields, &update) || !update.has_value) { malformed(); break; } + StackValue *value = nullptr; + if (message->type == debug_Command_COMMAND_UPDATE_LOCAL) { + ExecutionContext *ectx = m->warduino->execution_context; + if (ectx->fp + static_cast(update.index) > ectx->sp) { malformed(); break; } + value = &ectx->stack[ectx->fp + update.index]; + } else if (message->type == debug_Command_COMMAND_UPDATE_GLOBAL) { + if (update.index >= m->global_count) { malformed(); break; } + value = m->globals[update.index]->value; + } else { + ExecutionContext *ectx = m->warduino->execution_context; + if (update.index > static_cast(ectx->sp)) { malformed(); break; } + value = &ectx->stack[update.index]; + } + const bool success = valueFromProto(update.value, value); + if (!success) malformed(); else sendOperationResult(message->type, true); break; - case interruptUPDATEModule: - handleUpdateModule(m, interruptData); - this->channel->write("CHANGE Module!\n"); - free(interruptData); + } + case debug_Command_COMMAND_UPDATE_MODULE: { + debug_ModuleUpdate update = debug_ModuleUpdate_init_zero; + std::vector wasm; + setDecodeCallback(&update.wasm, &wasm); + if (!decodePayload(message->payload, debug_ModuleUpdate_fields, &update) || wasm.empty()) { malformed(); break; } + auto *copy = static_cast(malloc(wasm.size())); + if (copy == nullptr) { sendOperationResult(message->type, false); break; } + memcpy(copy, wasm.data(), wasm.size()); + m->warduino->update_module(m, copy, wasm.size()); + sendOperationResult(message->type, true); + break; + } + case debug_Command_COMMAND_UPDATE_FUNCTION: { + debug_Function update = debug_Function_init_zero; + std::vector instructions; + setDecodeCallback(&update.instructions, &instructions); + if (!decodePayload(message->payload, debug_Function_fields, &update) || + update.function_index >= m->function_count || instructions.empty() || + instructions.back() != 0x0b) { malformed(); break; } + functionBodies[update.function_index] = std::move(instructions); + Block &function = m->functions[update.function_index]; + function.start_ptr = functionBodies[update.function_index].data(); + function.end_ptr = function.start_ptr + functionBodies[update.function_index].size() - 1; + function.br_ptr = function.end_ptr; + sendOperationResult(message->type, true); break; - case interruptUPDATEGlobal: - this->handleUpdateGlobalValue(m, interruptData + 1); - free(interruptData); + } + case debug_Command_COMMAND_UPDATE_CALLBACKS: { + debug_CallbackMapping mapping = debug_CallbackMapping_init_zero; + std::vector entries; + mapping.entries.funcs.decode = collectCallbackEntries; + mapping.entries.arg = &entries; + if (!decodePayload(message->payload, debug_CallbackMapping_fields, &mapping)) { malformed(); break; } + CallbackHandler::clear_callbacks(); + for (const auto &entry : entries) { + for (uint32_t index : entry.indexes) CallbackHandler::add_callback(Callback(m, entry.topic, index)); + } + sendOperationResult(message->type, true); break; - case interruptUPDATEStackValue: - this->handleUpdateStackValue(m, interruptData + 1); - free(interruptData); + } + case debug_Command_COMMAND_SET_SNAPSHOT_POLICY: { + debug_SnapshotPolicyConfig config = debug_SnapshotPolicyConfig_init_zero; + std::vector selectedState; + setDecodeCallback(&config.selected_state, &selectedState); + if (!decodePayload(message->payload, debug_SnapshotPolicyConfig_fields, &config) || + config.policy > debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING) { malformed(); break; } + snapshotPolicy = static_cast(config.policy); + checkpointInterval = config.interval == 0 ? 1 : config.interval; + min_return_values = config.minimum_return_count; + free(checkpoint_state); + checkpoint_state = nullptr; + checkpoint_state_size = static_cast(selectedState.size()); + if (!selectedState.empty()) { + checkpoint_state = static_cast(malloc(selectedState.size())); + if (checkpoint_state == nullptr) { sendOperationResult(message->type, false); break; } + memcpy(checkpoint_state, selectedState.data(), selectedState.size()); + } + if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); + sendOperationResult(message->type, true); break; - case interruptINVOKE: - this->handleInvoke(m, interruptData + 1); - free(interruptData); + } + case debug_Command_COMMAND_SET_OVERRIDE: + case debug_Command_COMMAND_REMOVE_OVERRIDE: { + debug_Override request = debug_Override_init_zero; + std::vector nameBytes; + std::vector words; + setDecodeCallback(&request.primitive_name, &nameBytes); + request.argument_words.funcs.decode = collectWords; + request.argument_words.arg = &words; + if (!decodePayload(message->payload, debug_Override_fields, &request)) { malformed(); break; } + const auto fidx = findImportedFunction(m, std::string(nameBytes.begin(), nameBytes.end())); + if (!fidx || words.size() != m->functions[*fidx].type->param_count) { sendOperationResult(message->type, false); break; } + words.push_back(*fidx); + if (message->type == debug_Command_COMMAND_SET_OVERRIDE) overrides[words] = request.result; + else if (overrides.erase(words) == 0) { sendOperationResult(message->type, false); break; } + sendOperationResult(message->type, true); break; - case interruptSnapshot: - this->pauseRuntime(m); - free(interruptData); + } + case debug_Command_COMMAND_INSPECT: { + debug_Inspect inspectRequest = debug_Inspect_init_zero; + std::vector ignored; + setDecodeCallback(&inspectRequest.state, &ignored); + if (!decodePayload(message->payload, debug_Inspect_fields, &inspectRequest)) { malformed(); break; } snapshot(m); - this->channel->write("\n"); break; - case interruptSetSnapshotPolicy: - setSnapshotPolicy(m, interruptData + 1); - free(interruptData); + } + case debug_Command_COMMAND_LOAD_SNAPSHOT: { + debug_Snapshot state = debug_Snapshot_init_zero; + if (!decodePayload(message->payload, debug_Snapshot_fields, &state) || + !isToPhysicalAddrPossible(state.program_counter, m)) { malformed(); break; } + pauseRuntime(m); + m->warduino->execution_context->pc_ptr = toPhysicalAddress(state.program_counter, m); + sendOperationResult(message->type, true); break; - case interruptInspect: { - uint8_t *data = interruptData + 1; - uint16_t numberBytes = read_B16(&data); - uint8_t *state = interruptData + 3; - inspect(m, numberBytes, state); - this->channel->write("\n"); - free(interruptData); + } + case debug_Command_COMMAND_ADD_PROXY: + case debug_Command_COMMAND_REMOVE_PROXY: { + debug_FunctionRef reference = debug_FunctionRef_init_zero; + if (!decodePayload(message->payload, debug_FunctionRef_fields, &reference) || + supervisor == nullptr || reference.function_index >= m->function_count) { sendOperationResult(message->type, false); break; } + if (message->type == debug_Command_COMMAND_ADD_PROXY) supervisor->registerProxiedCall(reference.function_index); + else supervisor->unregisterProxiedCall(reference.function_index); + sendOperationResult(message->type, true); break; } - case interruptLoadSnapshot: - if (!this->receivingData) { - this->pauseRuntime(m); - debug("paused program execution\n"); - CallbackHandler::manual_event_resolution = true; - dbg_info("Manual event resolution is on."); - this->receivingData = true; - this->freeState(m, interruptData); - free(interruptData); - this->channel->write("ack!\n"); - } else { - debug("receiving state\n"); - receivingData = !this->saveState(m, interruptData); - free(interruptData); - debug("sending %s!\n", receivingData ? "ack" : "done"); - this->channel->write("%s!\n", receivingData ? "ack" : "done"); + case debug_Command_COMMAND_PROXY_CALL: + case debug_Command_COMMAND_INVOKE: { + debug_RemoteFunctionCall call = debug_RemoteFunctionCall_init_zero; + std::vector values; + call.arguments.funcs.decode = collectValues; + call.arguments.arg = &values; + if (!decodePayload(message->payload, debug_RemoteFunctionCall_fields, &call) || + call.function_index >= m->function_count || + values.size() != m->functions[call.function_index].type->param_count) { malformed(); break; } + auto *arguments = new StackValue[values.size()]; + bool valid = true; + for (size_t index = 0; index < values.size(); ++index) valid &= valueFromProto(values[index], &arguments[index]); + if (!valid) { delete[] arguments; malformed(); break; } + if (message->type == debug_Command_COMMAND_PROXY_CALL) { + if (proxy == nullptr) { delete[] arguments; sendOperationResult(message->type, false); break; } + proxy->pushRFC(m, new RFC(call.function_index, m->functions[call.function_index].type, arguments)); + break; } - break; - case interruptProxyCall: { - this->handleProxyCall(m, program_state, interruptData + 1); - free(interruptData); - } break; - case interruptMonitorProxies: { - debug("receiving functions list to proxy\n"); - this->handleMonitorProxies(m, interruptData + 1); - free(interruptData); - } break; - case interruptProxify: { - dbg_info("Converting to proxy settings.\n"); - this->proxify(); - free(interruptData); + const RunningState current = m->warduino->program_state; + m->warduino->program_state = WARDUINOrun; + m->warduino->invoke(m, call.function_index, static_cast(values.size()), arguments); + m->warduino->program_state = current; + debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; + result.success = true; + sendNotification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, + debug_RemoteFunctionResult_fields, &result); break; } - case interruptDUMPAllEvents: - debug("InterruptDUMPEvents\n"); - size = static_cast(CallbackHandler::event_count()); - [[fallthrough]]; - case interruptDUMPEvents: - // TODO get start and size from message - this->channel->write("{"); - this->dumpEvents(start, size); - this->channel->write("}\n"); - free(interruptData); - break; - case interruptPOPEvent: - CallbackHandler::resolve_event(true); - free(interruptData); - break; - case interruptPUSHEvent: - this->handlePushedEvent(reinterpret_cast(interruptData)); - free(interruptData); + case debug_Command_COMMAND_PROXIFY: + if (!requireEmpty()) break; + proxify(); + sendOperationResult(message->type, true); break; - case interruptRecvCallbackmapping: - Debugger::updateCallbackmapping( - m, reinterpret_cast(interruptData + 2)); - free(interruptData); + case debug_Command_COMMAND_POP_EVENT: + if (!requireEmpty()) break; + sendOperationResult(message->type, CallbackHandler::resolve_event(true)); break; - case interruptDUMPCallbackmapping: - this->dumpCallbackmapping(); - free(interruptData); + case debug_Command_COMMAND_PUSH_EVENT: { + debug_Event event = debug_Event_init_zero; + std::vector topic; + std::vector payload; + setDecodeCallback(&event.topic, &topic); + setDecodeCallback(&event.payload, &payload); + if (!decodePayload(message->payload, debug_Event_fields, &event) || topic.empty()) { malformed(); break; } + CallbackHandler::push_event(std::string(topic.begin(), topic.end()), + reinterpret_cast(payload.data()), payload.size()); + notifyPushedEvent(); break; - case interruptSetOverridePinValue: - this->addOverride(m, interruptData + 1); - free(interruptData); - break; - case interruptUnsetOverridePinValue: - this->removeOverride(m, interruptData + 1); - free(interruptData); + } + case debug_Command_COMMAND_RESET: + if (!requireEmpty()) break; + sendOperationResult(message->type, reset(m)); break; default: - // handle later - this->channel->write("COULD not parse interrupt data!\n"); - free(interruptData); + malformed(); break; } - fflush(stdout); return true; } // Private methods -void Debugger::printValue(const StackValue *v, const uint32_t idx, - const bool end = false) const { - char buff[256]; - -#define FMT(fmt0) "%" fmt0 - - switch (v->value_type) { - case I32: - snprintf(buff, 255, R"("type":"i32","value":)" FMT(PRIu32), - v->value.uint32); - break; - case I64: - snprintf(buff, 255, R"("type":"i64","value":)" FMT(PRIu64), - v->value.uint64); - break; - case F32: - snprintf(buff, 255, R"("type":"F32","value":")" FMT(PRIu32) "\"", - v->value.uint32); - break; - case F64: - snprintf(buff, 255, R"("type":"F64","value":")" FMT(PRIu64) "\"", - v->value.uint64); - break; - default: - snprintf(buff, 255, R"("type":"%02x","value":")" FMT(PRIu64) "\"", - v->value_type, v->value.uint64); - } - this->channel->write(R"({"idx":%d,%s}%s)", idx, buff, end ? "" : ","); -} +void Debugger::printValue(const StackValue *, const uint32_t, const bool) const {} uint8_t *Debugger::findOpcode(Module *m, const Block *block) { const auto find = @@ -450,7 +613,6 @@ void Debugger::handleInvoke(Module *m, uint8_t *interruptData) const { void Debugger::handleInterruptRUN(const Module *m, RunningState *program_state) { ExecutionContext *ectx = m->warduino->execution_context; - this->channel->write("GO!\n"); if (*program_state == WARDUINOpause && this->isBreakpoint(ectx->pc_ptr)) { this->skipBreakpoint = ectx->pc_ptr; } @@ -496,193 +658,49 @@ void Debugger::handleInterruptBP(Module *m, uint8_t *interruptData) { this->deleteBreakpoint(bpt); } } - this->channel->write("BP %" PRIu32 "!\n", virtualAddress); + debug("BP %" PRIu32 "!\n", virtualAddress); } -void Debugger::dump(Module *m, bool full) const { - ExecutionContext *ectx = m->warduino->execution_context; - auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; - this->channel->write("{"); - - // current PC - this->channel->write("\"pc\":%" PRIu32 ",", toVA(ectx->pc_ptr)); - - this->dumpBreakpoints(m); - - this->dumpFunctions(m); - - this->dumpCallstack(m); +void Debugger::dump(Module *m, bool) const { snapshot(m); } - if (full) { - this->channel->write(R"( "locals": )"); - this->dumpLocals(m); - this->channel->write(", "); - this->dumpEvents(0, static_cast(CallbackHandler::event_count())); - this->channel->write(", "); - } - - this->dumpHeapInfo(m); - - this->channel->write("}\n\n"); - // fflush(stdout); +void Debugger::dumpStack(const Module *) const { + debug_Locals locals = debug_Locals_init_zero; + sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, + debug_Locals_fields, &locals); } -void Debugger::dumpStack(const Module *m) const { - ExecutionContext *ectx = m->warduino->execution_context; - this->channel->write("{\"stack\": ["); - int32_t i = ectx->sp; - while (0 <= i) { - this->printValue(&ectx->stack[i], i, i < 1); - i--; - } - this->channel->write("]}\n\n"); -} +void Debugger::dumpBreakpoints(Module *) const {} -void Debugger::dumpBreakpoints(Module *m) const { - this->channel->write("\"breakpoints\":["); - { - size_t i = 0; - for (auto bp : this->breakpoints) { - this->channel->write("%" PRIu32 "%s", toVirtualAddress(bp, m), - (++i < this->breakpoints.size()) ? "," : ""); - } - } - this->channel->write("],"); -} - -void Debugger::dumpFunctions(Module *m) const { - this->channel->write("\"functions\":["); - - for (size_t i = m->import_count; i < m->function_count; i++) { - this->channel->write(R"({"fidx":"0x%x",)", m->functions[i].fidx); - this->channel->write("\"from\":%" PRIu32 ",\"to\":%" PRIu32 "}%s", - toVirtualAddress(m->functions[i].start_ptr, m), - toVirtualAddress(m->functions[i].end_ptr, m), - (i < m->function_count - 1) ? "," : "],"); - } -} +void Debugger::dumpFunctions(Module *) const {} /* * {"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"ra":"%p"}%s */ -void Debugger::dumpCallstack(Module *m) const { - ExecutionContext *ectx = m->warduino->execution_context; - auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; - this->channel->write("\"callstack\":["); +void Debugger::dumpCallstack(Module *) const {} - if (ectx->csp < 0) { - this->channel->write("]"); - return; - } - - for (int i = 0; i <= ectx->csp; i++) { - const Frame *f = &ectx->callstack[i]; - int callsite_retaddr = -1; - int retaddr = -1; - // first frame has no retrun address - if (f->ra_ptr != nullptr) { - uint8_t *callsite = nullptr; - callsite = f->ra_ptr - 2; // callsite of function (if type 0) - callsite_retaddr = static_cast(toVA(callsite)); - retaddr = static_cast(toVA(f->ra_ptr)); - } - this->channel->write(R"({"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,)", - f->block->block_type, f->block->fidx, f->sp, - f->fp); - this->channel->write("\"start\":%" PRIu32 - ",\"ra\":%d,\"callsite\":%d}%s", - toVA(f->block->start_ptr), retaddr, - callsite_retaddr, (i < ectx->csp) ? "," : "],"); - } -} - -void Debugger::dumpLocals(const Module *m) const { - // fflush(stdout); - ExecutionContext *ectx = m->warduino->execution_context; - int firstFunFramePtr = ectx->csp; - - if (firstFunFramePtr < 0) { - this->channel->write("[]"); - return; - } - - while (ectx->callstack[firstFunFramePtr].block->block_type != 0) { - firstFunFramePtr--; - if (firstFunFramePtr < 0) { - FATAL("Not in a function!"); - } - } - Frame *f = &ectx->callstack[firstFunFramePtr]; - this->channel->write(R"({"count":%u,"locals":[)", f->block->local_count); - // fflush(stdout); // FIXME: this is needed for ESP to properly print - for (uint32_t i = 0; i < f->block->local_count; i++) { - char _value_str[256]; - auto v = &ectx->stack[ectx->fp + i]; - switch (v->value_type) { - case I32: - snprintf(_value_str, 255, - R"("type":"i32","value":)" FMT(PRIu32), - v->value.uint32); - break; - case I64: - snprintf(_value_str, 255, - R"("type":"i64","value":)" FMT(PRIu64), - v->value.uint64); - break; - case F32: - snprintf(_value_str, 255, R"("type":"F32","value":%.7f)", - v->value.f32); - break; - case F64: - snprintf(_value_str, 255, R"("type":"F64","value":%.7f)", - v->value.f64); - break; - default: - snprintf(_value_str, 255, - R"("type":"%02x","value":")" FMT(PRIu64) "\"", - v->value_type, v->value.uint64); - } - - this->channel->write("{%s, \"index\":%u}%s", _value_str, - i + f->block->type->param_count, - (i + 1 < f->block->local_count) ? "," : ""); - } - this->channel->write("]}"); - // fflush(stdout); -#undef FMT +void Debugger::dumpLocals(const Module *) const { + debug_Locals locals = debug_Locals_init_zero; + sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, + debug_Locals_fields, &locals); } void Debugger::dumpEvents(long start, long size) const { - bool previous = CallbackHandler::resolving_event; - CallbackHandler::resolving_event = true; - if (size > EVENTS_SIZE) { - size = EVENTS_SIZE; - } - - this->channel->write(R"("events": [)"); - long index = start, end = start + size; - std::for_each(CallbackHandler::event_begin() + start, - CallbackHandler::event_begin() + end, - [this, &index, &end](const Event &e) { - this->channel->write( - R"({"topic": "%s", "payload": "%s"})", - e.topic.c_str(), e.payload.c_str()); - if (++index < end) { - this->channel->write(", "); - } - }); - this->channel->write("]"); - - CallbackHandler::resolving_event = previous; + debug_EventsQueue events = debug_EventsQueue_init_zero; + events.total_count = static_cast(CallbackHandler::event_count()); + events.has_range = true; + events.range.start = start < 0 ? 0 : static_cast(start); + events.range.end = size < 0 ? events.range.start : events.range.start + static_cast(size); + sendNotification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, + debug_EventsQueue_fields, &events); } void Debugger::dumpCallbackmapping() const { - this->channel->write("%s\n", CallbackHandler::dump_callbacks().c_str()); + debug_CallbackMapping callbacks = debug_CallbackMapping_init_zero; + sendNotification(debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP, + debug_CallbackMapping_fields, &callbacks); } -void Debugger::dumpHeapInfo(Module *m) const { - this->channel->write(R"("heap":{"used":%u})", m->warduino->get_heap_used()); -} +void Debugger::dumpHeapInfo(Module *) const {} /** * Read the change in bytes array. @@ -752,10 +770,10 @@ bool Debugger::handleChangedFunction(const Module *m, uint8_t *bytes) { bool Debugger::handleChangedLocal(const Module *m, uint8_t *bytes) const { if (*bytes != interruptUPDATELocal) return false; uint8_t *pos = bytes + 1; - this->channel->write("Local updates: %x\n", *pos); + debug("Local updates: %x\n", *pos); uint32_t localId = read_LEB_32(&pos); - this->channel->write("Local %u being changed\n", localId); + debug("Local %u being changed\n", localId); ExecutionContext *ectx = m->warduino->execution_context; auto v = &ectx->stack[ectx->fp + localId]; switch (v->value_type) { @@ -774,236 +792,35 @@ bool Debugger::handleChangedLocal(const Module *m, uint8_t *bytes) const { default: // nothing to do :( break; } - this->channel->write("Local %u changed to %u\n", localId, v->value.uint32); + debug("Local %u changed to %u\n", localId, v->value.uint32); return true; } void Debugger::notifyPushedEvent() const { - this->channel->write("new pushed event\n"); + this->sendNotification(debug_NotificationType_NOTIFICATION_NEW_EVENT); } -bool Debugger::handlePushedEvent(char *bytes) const { - if (*bytes != interruptPUSHEvent) return false; - auto parsed = nlohmann::json::parse(bytes + 1); - debug("handle pushed event: %s\n", bytes + 1); - auto *event = new Event(*parsed.find("topic"), *parsed.find("payload")); - CallbackHandler::push_event(event); - this->notifyPushedEvent(); - return true; -} +bool Debugger::handlePushedEvent(char *) const { return false; } void Debugger::snapshot(Module *m) const { - uint16_t numberBytes = 12; - uint8_t state[] = {pcState, - breakpointsState, - callstackState, - globalsState, - tableState, - memoryState, - branchingTableState, - stackState, - callbacksState, - eventsState, - ioState, - overridesState}; - inspect(m, numberBytes, state); -} - -void Debugger::inspect(Module *m, const uint16_t sizeStateArray, - const uint8_t *state) const { ExecutionContext *ectx = m->warduino->execution_context; - debug("asked for inspect\n"); - uint16_t idx = 0; - auto toVA = [m](uint8_t *addr) { return toVirtualAddress(addr, m); }; - bool addComma = false; - - this->channel->write("{"); - - while (idx < sizeStateArray) { - switch (state[idx++]) { - case pcState: { // PC - this->channel->write("\"pc\":%" PRIu32 "", toVA(ectx->pc_ptr)); - addComma = true; - - break; - } - case breakpointsState: { - this->channel->write("%s\"breakpoints\":[", - addComma ? "," : ""); - addComma = true; - size_t i = 0; - for (auto bp : this->breakpoints) { - this->channel->write( - "%" PRIu32 "%s", toVA(bp), - (++i < this->breakpoints.size()) ? "," : ""); - } - this->channel->write("]"); - break; - } - case callstackState: { - this->channel->write("%s\"callstack\":[", addComma ? "," : ""); - addComma = true; - for (int j = 0; j <= ectx->csp; j++) { - const Frame *f = &ectx->callstack[j]; - const uint8_t bt = f->block->block_type; - const uint32_t block_key = - (bt == 0 || bt == 0xff || bt == 0xfe) - ? 0 - : toVA(findOpcode(m, f->block)); - const uint32_t fidx = bt == 0 ? f->block->fidx : 0; - const auto ra = f->ra_ptr == nullptr ? -1 : toVA(f->ra_ptr); - this->channel->write( - R"({"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"idx":%d,)", - bt, fidx, f->sp, f->fp, j); - this->channel->write( - "\"block_key\":%" PRIu32 ",\"ra\":%d}%s", block_key, ra, - (j < ectx->csp) ? "," : ""); - } - this->channel->write("]"); - break; - } - case stackState: { - this->channel->write("%s\"stack\":[", addComma ? "," : ""); - addComma = true; - for (int j = 0; j <= ectx->sp; j++) { - auto v = &ectx->stack[j]; - printValue(v, j, j == ectx->sp); - } - this->channel->write("]"); - break; - } - case globalsState: { - this->channel->write("%s\"globals\":[", addComma ? "," : ""); - addComma = true; - for (uint32_t j = 0; j < m->global_count; j++) { - auto v = (*(m->globals + j))->value; - printValue(v, j, j == (m->global_count - 1)); - } - this->channel->write("]"); // closing globals - break; - } - case tableState: { - this->channel->write( - R"(%s"table":{"max":%d, "init":%d, "elements":[)", - addComma ? "," : "", m->table.maximum, m->table.initial); - addComma = true; - for (uint32_t j = 0; j < m->table.size; j++) { - this->channel->write("%" PRIu32 "%s", m->table.entries[j], - (j + 1) == m->table.size ? "" : ","); - } - this->channel->write("]}"); // closing table - break; - } - case branchingTableState: { - this->channel->write( - R"(%s"br_table":{"size":"0x%x","labels":[)", - addComma ? "," : "", BR_TABLE_SIZE); - for (uint32_t j = 0; j < BR_TABLE_SIZE; j++) { - this->channel->write("%" PRIu32 "%s", ectx->br_table[j], - (j + 1) == BR_TABLE_SIZE ? "" : ","); - } - this->channel->write("]}"); - break; - } - case memoryState: { - uint32_t total_elems = - m->memory.pages * static_cast(PAGE_SIZE); - this->channel->write( - R"(%s"memory":{"pages":%d,"max":%d,"init":%d,"bytes":[)", - addComma ? "," : "", m->memory.pages, m->memory.maximum, - m->memory.initial); - addComma = true; - if (total_elems != 0) { - uint8_t data = m->memory.bytes[0]; - uint32_t count = 1; - bool arrayComma = false; - for (uint32_t j = 1; j < total_elems; j++) { - if (m->memory.bytes[j] == data) { - count++; - } else { - this->channel->write("%s%" PRIu8 ",%d", - arrayComma ? "," : "", data, - count); - arrayComma = true; - data = m->memory.bytes[j]; - count = 1; - } - } - this->channel->write("%s%" PRIu8 ",%d", - arrayComma ? "," : "", data, count); - } - this->channel->write("]}"); // closing memory - break; - } - case callbacksState: { - bool noOuterBraces = false; - this->channel->write( - "%s%s", addComma ? "," : "", - CallbackHandler::dump_callbacksV2(noOuterBraces).c_str()); - addComma = true; - break; - } - case eventsState: { - this->channel->write("%s", addComma ? "," : ""); - this->dumpEvents( - 0, static_cast(CallbackHandler::event_count())); - addComma = true; - break; - } - case ioState: { - this->channel->write("%s", addComma ? "," : ""); - this->channel->write("\"io\": ["); - bool comma = false; - std::vector external_state = - m->warduino->interpreter->get_io_state(m); - for (auto state_elem : external_state) { - this->channel->write("%s{", comma ? ", " : ""); - this->channel->write( - R"("key": "%s", "output": %s, "value": %d)", - state_elem->key.c_str(), - state_elem->output ? "true" : "false", - state_elem->value); - this->channel->write("}"); - comma = true; - delete state_elem; - } - this->channel->write("]"); - addComma = true; - break; - } - case overridesState: { - this->channel->write("%s", addComma ? "," : ""); - this->channel->write(R"("overrides": [)"); - bool comma = false; - for (const auto &[key, return_value] : overrides) { - this->channel->write("%s", comma ? ", " : ""); - const uint32_t fidx = key[key.size() - 1]; - this->channel->write(R"({"fidx": %d, "args": [)", fidx); - for (uint32_t i = 0; i < key.size() - 1; i++) { - this->channel->write("%s%d", i > 0 ? ", " : "", key[i]); - } - this->channel->write(R"(], "return_value": %d})", - return_value); - comma = true; - } - this->channel->write("]"); - addComma = true; - break; - } - case heapState: { - uint32_t heap_used = m->warduino->get_heap_used(); - this->channel->write(R"(%s"heap":{"used":%d})", - addComma ? "," : "", heap_used); - addComma = true; - break; - } - default: { - debug("dumpExecutionState: Received unknown state request\n"); - break; - } - } + debug_Snapshot state = debug_Snapshot_init_zero; + state.program_counter = toVirtualAddress(ectx->pc_ptr, m); + state.heap_used = m->warduino->get_heap_used(); + switch (m->warduino->program_state) { + case WARDUINOrun: state.state = debug_State_STATE_WARDUINO_RUN; break; + case WARDUINOpause: state.state = debug_State_STATE_WARDUINO_PAUSE; break; + case WARDUINOstep: state.state = debug_State_STATE_WARDUINO_STEP; break; + case PROXYrun: state.state = debug_State_STATE_PROXY_RUN; break; + case PROXYhalt: state.state = debug_State_STATE_PROXY_HALT; break; + default: state.state = debug_State_STATE_WARDUINO_PAUSE; break; } - this->channel->write("}"); + sendNotification(debug_NotificationType_NOTIFICATION_SNAPSHOT, + debug_Snapshot_fields, &state); +} + +void Debugger::inspect(Module *m, const uint16_t, const uint8_t *) const { + snapshot(m); } void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) { @@ -1061,9 +878,9 @@ std::optional getPrimitiveBeingCalled(Module *m, uint8_t *pc_ptr) { void Debugger::handleSnapshotPolicy(Module *m) { if (snapshotPolicy == SnapshotPolicy::atEveryInstruction) { - this->channel->write("SNAPSHOT "); + debug("SNAPSHOT "); snapshot(m); - this->channel->write("\n"); + debug("\n"); } else if (snapshotPolicy == SnapshotPolicy::checkpointing) { if (instructions_executed >= checkpointInterval || fidx_called) { if (min_return_values == 0) { @@ -1102,45 +919,23 @@ void Debugger::handleSnapshotPolicy(Module *m) { } } } else if (snapshotPolicy != SnapshotPolicy::none) { - this->channel->write("WARNING: Invalid snapshot policy."); + debug("WARNING: Invalid snapshot policy."); } } -void Debugger::checkpoint(Module *m, const bool force, const bool full) { - if (instructions_executed == 0 && !force) { - return; - } +void Debugger::checkpoint(Module *, const bool force, const bool full) { + if (instructions_executed == 0 && !force) { + return; + } - this->channel->write(R"(CHECKPOINT {"instructions_executed": %d, )", - instructions_executed); + debug_Checkpoint notification = debug_Checkpoint_init_zero; + notification.instruction_count = instructions_executed; if (fidx_called) { - this->channel->write(R"("fidx_called": %d, "args": [)", *fidx_called); - const Block &func_block = m->functions[*fidx_called]; - bool comma = false; - for (uint32_t i = 0; i < func_block.type->param_count; i++) { - channel->write("%s%d", comma ? ", " : "", prim_args[i]); - comma = true; - } - this->channel->write("], "); - - // Return values: - this->channel->write(R"("returns": [)"); - comma = false; - for (uint32_t i = 0; i < func_block.type->result_count; i++) { - ExecutionContext *ectx = m->warduino->execution_context; - channel->write("%s%d", comma ? ", " : "", - ectx->stack[ectx->sp - i].value.uint32); - comma = true; - } - this->channel->write("], "); - } - this->channel->write(R"("snapshot": )"); - if (!checkpoint_state || full) { - snapshot(m); - } else { - inspect(m, checkpoint_state_size, checkpoint_state); + notification.has_primitive_call = true; + notification.primitive_function_index = *fidx_called; } - this->channel->write("}\n"); + sendNotification(debug_NotificationType_NOTIFICATION_CHECKPOINT, + debug_Checkpoint_fields, ¬ification); instructions_executed = 0; } @@ -1568,10 +1363,14 @@ RFC *Debugger::topProxyCall() const { } void Debugger::sendProxyCallResult(Module *m) const { - if (proxy == nullptr) { - return; - } - this->proxy->returnResult(m); + if (proxy == nullptr) return; + RFC *rfc = proxy->returnResult(m); + if (rfc == nullptr) return; + debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; + result.success = rfc->success; + sendNotification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, + debug_RemoteFunctionResult_fields, &result); + delete rfc; } bool Debugger::isProxy() const { return this->proxy != nullptr; } @@ -1592,7 +1391,7 @@ void Debugger::handleMonitorProxies(const Module *m, m->warduino->debugger->supervisor->registerProxiedCall(fidx); } - this->channel->write("done!\n"); + debug("done!\n"); } void Debugger::startProxySupervisor(Channel *socket) { @@ -1612,17 +1411,8 @@ void Debugger::disconnect_proxy() const { this->supervisor->thread.join(); } -void Debugger::updateCallbackmapping(Module *m, const char *interruptData) { - nlohmann::basic_json<> parsed = nlohmann::json::parse(interruptData); - CallbackHandler::clear_callbacks(); - nlohmann::basic_json<> callbacks = *parsed.find("callbacks"); - for (auto &array : callbacks.items()) { - auto callback = array.value().begin(); - for (auto &functions : callback.value().items()) { - CallbackHandler::add_callback( - Callback(m, callback.key(), functions.value())); - } - } +void Debugger::updateCallbackmapping(Module *, const char *) { + // Legacy JSON callback mapping input is intentionally unsupported. } // Stop the debugger @@ -1650,16 +1440,16 @@ bool Debugger::handleUpdateModule(Module *m, uint8_t *data) { } bool Debugger::handleUpdateGlobalValue(const Module *m, uint8_t *data) const { - this->channel->write("Global updates: %x\n", *data); + debug("Global updates: %x\n", *data); const uint32_t index = read_LEB_32(&data); if (index >= m->global_count) return false; - this->channel->write("Global %u being changed\n", index); + debug("Global %u being changed\n", index); StackValue *v = m->globals[index]->value; constexpr bool decodeType = false; deserialiseStackValue(data, decodeType, v); - this->channel->write("Global %u changed to %u\n", index, v->value.uint32); + debug("Global %u changed to %u\n", index, v->value.uint32); return true; } @@ -1675,7 +1465,7 @@ bool Debugger::handleUpdateStackValue(const Module *m, uint8_t *bytes) const { if (!deserialiseStackValue(bytes, decodeType, sv)) { return false; } - this->channel->write("StackValue %" PRIu32 " changed\n", idx); + debug("StackValue %" PRIu32 " changed\n", idx); return true; } @@ -1683,7 +1473,7 @@ bool Debugger::reset(Module *m) { m->warduino->reset_module(m); instructions_executed = 0; instructions_since_full_snapshot = 0; - this->channel->write("Reset WARDuino.\n"); + debug("Reset WARDuino.\n"); return true; } @@ -1712,10 +1502,10 @@ void Debugger::addOverride(Module *m, uint8_t *interruptData) { const std::optional fidx = resolve_imported_function(m, primitive_name); if (!fidx) { - channel->write( + debug( "Cannot override the result for unknown function \"%s\".\n", primitive_name.c_str()); - channel->write("ack%x;0\n", interruptUnsetOverridePinValue); + debug("ack%x;0\n", interruptUnsetOverridePinValue); return; } @@ -1727,7 +1517,7 @@ void Debugger::addOverride(Module *m, uint8_t *interruptData) { key[param_count] = fidx.value(); const uint32_t result = read_B32(&interruptData); - channel->write("ack%x;1\n", interruptSetOverridePinValue); + debug("ack%x;1\n", interruptSetOverridePinValue); overrides[key] = result; } @@ -1736,9 +1526,9 @@ void Debugger::removeOverride(Module *m, uint8_t *interruptData) { const std::optional fidx = resolve_imported_function(m, primitive_name); if (!fidx) { - channel->write("Cannot remove override for unknown function \"%s\".\n", + debug("Cannot remove override for unknown function \"%s\".\n", primitive_name.c_str()); - channel->write("ack%x;0\n", interruptUnsetOverridePinValue); + debug("ack%x;0\n", interruptUnsetOverridePinValue); return; } @@ -1750,10 +1540,10 @@ void Debugger::removeOverride(Module *m, uint8_t *interruptData) { key[param_count] = fidx.value(); if (overrides.erase(key) == 0) { - channel->write("ack%x;0\n", interruptUnsetOverridePinValue); + debug("ack%x;0\n", interruptUnsetOverridePinValue); return; } - channel->write("ack%x;1\n", interruptUnsetOverridePinValue); + debug("ack%x;1\n", interruptUnsetOverridePinValue); } bool Debugger::getMockForArgs(Module *m, uint32_t fidx, uint32_t &result) { @@ -1780,7 +1570,7 @@ bool Debugger::handleContinueFor(Module *m) { if (snapshotPolicy == SnapshotPolicy::checkpointing) { checkpoint(m); } - this->channel->write("DONE!\n"); + this->sendNotification(debug_NotificationType_NOTIFICATION_PAUSED); pauseRuntime(m); return true; } @@ -1794,7 +1584,7 @@ void Debugger::notifyCompleteStep(Module *m) const { SnapshotPolicy::checkpointing) { m->warduino->debugger->checkpoint(m); } - this->channel->write("STEP!\n"); + this->sendNotification(debug_NotificationType_NOTIFICATION_STEPPED); } Debugger::~Debugger() { diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 193ee65d1..fc73fe889 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -16,13 +17,19 @@ #include "../Threading/warduino-thread.h" #include "../Utils/sockets.h" -#include -#include "debug.pb.h" +#include "nanopb/debug.pb.h" +#include "nanopb/pb_decode.h" +#include "nanopb/pb_encode.h" struct Module; struct Block; struct StackValue; +struct DebugMessage { + debug_Command type; + std::vector payload; +}; + enum operation { STORE = 0, LOAD = 1, @@ -135,17 +142,14 @@ struct FNV1aVectorHash { class Debugger { private: - std::deque debugMessages = {}; - - // Help variables - volatile bool interruptWrite{}; - volatile bool interruptRead{}; - bool interruptEven = true; - uint8_t interruptLastChar{}; - std::vector interruptBuffer; - std::queue parsedInterrupts{}; - long interruptSize{}; - bool receivingData = false; + std::deque debugMessages = {}; + + // Incomplete bytes from the binary framed stream. + std::vector pendingFrameBytes; + static constexpr size_t maxFramePayload = 65536; + + // Function replacement storage must outlive decoded queue frames. + std::unordered_map> functionBodies; Proxy *proxy = nullptr; // proxy module for debugger @@ -178,7 +182,12 @@ class Debugger { // TODO Move parsing to WARDuino class? void parseDebugBuffer(size_t len, const uint8_t *buff); - void pushMessage(uint8_t *msg); + void pushMessage(DebugMessage msg); + + bool sendNotification(debug_NotificationType type, + const pb_msgdesc_t *fields = nullptr, + const void *payload = nullptr) const; + void sendOperationResult(debug_Command command, bool success) const; //// Handle REPL interrupts @@ -284,7 +293,7 @@ class Debugger { void addDebugMessage(size_t len, const uint8_t *buff); - uint8_t *getDebugMessage(); + std::optional getDebugMessage(); bool checkDebugMessages(Module *m, RunningState *program_state); diff --git a/src/Edward/proxy.cpp b/src/Edward/proxy.cpp index 5b9021248..1b92d2482 100644 --- a/src/Edward/proxy.cpp +++ b/src/Edward/proxy.cpp @@ -48,31 +48,12 @@ void Proxy::pushRFC(Module *m, RFC *rfc) { RFC *Proxy::topRFC() { return this->calls->top(); } -void Proxy::returnResult(Module *m) { +RFC *Proxy::returnResult(Module *m) { + (void)m; + if (this->calls->empty()) return nullptr; RFC *rfc = this->calls->top(); - - // remove call from lifo queue this->calls->pop(); - - if (!rfc->success) { - // TODO exception msg - WARDuino::instance()->debugger->channel->write(R"({"success":false})"); - return; - } - - if (rfc->type->result_count == 0) { - // reading result from stack - WARDuino::instance()->debugger->channel->write(R"({"success":true})"); - return; - } - - // send the result to the client - ExecutionContext *ectx = m->warduino->execution_context; - rfc->result = &ectx->stack[ectx->sp]; - char *val = printValue(rfc->result); - WARDuino::instance()->debugger->channel->write(R"({"success":true,%s})", - val); - free(val); + return rfc; } char *printValue(StackValue *v) { diff --git a/src/Edward/proxy.h b/src/Edward/proxy.h index 20993f504..a7f8224f5 100644 --- a/src/Edward/proxy.h +++ b/src/Edward/proxy.h @@ -21,7 +21,7 @@ class Proxy { void pushRFC(Module *m, RFC *rfc); RFC *topRFC(); - void returnResult(Module *m); + RFC *returnResult(Module *m); // Server side ( arduino side ) static StackValue *readRFCArgs(Block *func, uint8_t *data); diff --git a/src/Edward/proxy_supervisor.cpp b/src/Edward/proxy_supervisor.cpp index 511efca99..ba4246d88 100644 --- a/src/Edward/proxy_supervisor.cpp +++ b/src/Edward/proxy_supervisor.cpp @@ -135,7 +135,7 @@ bool ProxySupervisor::send( nlohmann::basic_json<> ProxySupervisor::readReply() { while (!this->hasReplied); - WARDuino::instance()->debugger->channel->write("read reply: succeeded\n"); + dbg_info("read reply: succeeded\n"); this->hasReplied = false; return this->proxyResult; } diff --git a/src/Utils/sockets.cpp b/src/Utils/sockets.cpp index fc846a890..1ed7b4ba4 100644 --- a/src/Utils/sockets.cpp +++ b/src/Utils/sockets.cpp @@ -8,9 +8,11 @@ #include #include +#include #include #include #include +#include #ifdef WIFI_ENABLED // Socket Debugger Interface @@ -97,6 +99,18 @@ int Sink::write(const char *fmt, ...) { return written; } +ssize_t Sink::writeBytes(const uint8_t *data, const size_t size) { + if (data == nullptr && size != 0) return -1; + size_t offset = 0; + while (offset < size) { + const size_t written = fwrite(data + offset, 1, size - offset, this->outStream); + if (written == 0) return -1; + offset += written; + } + fflush(this->outStream); + return static_cast(offset); +} + Duplex::Duplex(FILE *inStream, FILE *outStream) : Sink(outStream) { this->inDescriptor = fileno(inStream); } @@ -118,6 +132,17 @@ int FileDescriptorChannel::write(const char *fmt, ...) { return written; } +ssize_t FileDescriptorChannel::writeBytes(const uint8_t *data, const size_t size) { + size_t offset = 0; + while (offset < size) { + const ssize_t written = ::write(this->fd, data + offset, size - offset); + if (written > 0) { offset += static_cast(written); continue; } + if (written < 0 && errno == EINTR) continue; + return -1; + } + return static_cast(offset); +} + ssize_t FileDescriptorChannel::read(void *out, size_t size) { return ::read(this->fd, out, size); } @@ -174,6 +199,18 @@ int WebSocket::write(const char *fmt, ...) { return written; } +ssize_t WebSocket::writeBytes(const uint8_t *data, const size_t size) { + if (this->socket < 0) return -1; + size_t offset = 0; + while (offset < size) { + const ssize_t written = ::write(this->socket, data + offset, size - offset); + if (written > 0) { offset += static_cast(written); continue; } + if (written < 0 && errno == EINTR) continue; + return -1; + } + return static_cast(offset); +} + ssize_t WebSocket::read(void *out, size_t size) { if (this->socket < 0) { return 0; diff --git a/src/Utils/sockets.h b/src/Utils/sockets.h index bea15bb84..377f08307 100644 --- a/src/Utils/sockets.h +++ b/src/Utils/sockets.h @@ -3,6 +3,7 @@ #include #include +#include #ifdef __ZEPHYR__ #if IS_ENABLED(CONFIG_WIFI) @@ -31,6 +32,10 @@ class Channel { public: virtual void open() {} virtual int write(char const *, ...) { return 0; } + virtual ssize_t writeBytes(const uint8_t *data, size_t size) { + (void)data; + return static_cast(size); + } virtual ssize_t read(void *, size_t) { return 0; } virtual void close() {} virtual ~Channel() = default; @@ -47,6 +52,7 @@ class Sink : public Channel { public: explicit Sink(FILE *out); int write(char const *fmt, ...) override; + ssize_t writeBytes(const uint8_t *data, size_t size) override; }; class Duplex : public Sink { @@ -67,6 +73,7 @@ class FileDescriptorChannel : public Channel { explicit FileDescriptorChannel(int fileDescriptor); int write(char const *fmt, ...) override; + ssize_t writeBytes(const uint8_t *data, size_t size) override; ssize_t read(void *out, size_t size) override; }; @@ -81,6 +88,7 @@ class WebSocket : public Channel { void open() override; int write(char const *fmt, ...) override; + ssize_t writeBytes(const uint8_t *data, size_t size) override; ssize_t read(void *out, size_t size) override; void close() override; }; diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index 723c1384e..5fdebcc81 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -88,8 +88,7 @@ bool CallbackHandler::resolve_event(bool force) { CallbackHandler::events->empty()) { if (force) { printf("No events to be processed!\n"); - WARDuino::instance()->debugger->channel->write( - "no events to be processed"); + } return false; } @@ -97,9 +96,7 @@ bool CallbackHandler::resolve_event(bool force) { if (should_push_event()) { Event e = CallbackHandler::events->at(CallbackHandler::pushed_cursor++); - WARDuino::instance()->debugger->channel->write( - R"({"topic":"%s","payload":"%s"})", e.topic.c_str(), - e.payload.c_str()); + WARDuino::instance()->debugger->notifyPushedEvent(); CallbackHandler::events->pop_front(); CallbackHandler::pushed_cursor--; From 7a0005426bd3860b30fec198421121425c1ddf8d Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 19 Aug 2026 14:21:11 +0200 Subject: [PATCH 12/26] Lint --- .clang-format-ignore | 1 + src/Debug/debugger.cpp | 395 +++++++++++++++++++++++-------- src/Debug/debugger.h | 3 +- src/Utils/sockets.cpp | 22 +- src/Utils/sockets.h | 2 +- src/WARDuino/CallbackHandler.cpp | 1 - 6 files changed, 308 insertions(+), 116 deletions(-) create mode 100644 .clang-format-ignore diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 000000000..d3295e729 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1 @@ +src/Debug/debug.proto diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 222ffd400..43f04eeab 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -44,10 +44,16 @@ bool decodeFrameLength(const std::vector &bytes, size_t *headerSize, const size_t offset = i + 1; if (offset >= bytes.size()) return false; const uint8_t byte = bytes[offset]; - if (i == 4 && (byte & 0xf0U) != 0) { *headerSize = SIZE_MAX; return false; } + if (i == 4 && (byte & 0xf0U) != 0) { + *headerSize = SIZE_MAX; + return false; + } value |= static_cast(byte & 0x7fU) << (i * 7U); if ((byte & 0x80U) == 0) { - if (i > 0 && value < (1U << (i * 7U))) { *headerSize = SIZE_MAX; return false; } + if (i > 0 && value < (1U << (i * 7U))) { + *headerSize = SIZE_MAX; + return false; + } *headerSize = offset + 1; *payloadSize = value; return true; @@ -61,9 +67,10 @@ bool isKnownCommand(const uint8_t type) { } template -bool decodePayload(const std::vector &payload, const pb_msgdesc_t *fields, - T *message) { - pb_istream_t stream = pb_istream_from_buffer(payload.data(), payload.size()); +bool decodePayload(const std::vector &payload, + const pb_msgdesc_t *fields, T *message) { + pb_istream_t stream = + pb_istream_from_buffer(payload.data(), payload.size()); return pb_decode(&stream, fields, message); } @@ -86,14 +93,15 @@ void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { while (!pendingFrameBytes.empty()) { if (!isKnownCommand(pendingFrameBytes.front())) { pendingFrameBytes.clear(); - sendNotification(debug_NotificationType_NOTIFICATION_UNKNOWN_COMMAND); + sendNotification( + debug_NotificationType_NOTIFICATION_UNKNOWN_COMMAND); continue; } size_t headerSize = 0; size_t payloadSize = 0; - const bool completeLength = decodeFrameLength(pendingFrameBytes, &headerSize, - &payloadSize); + const bool completeLength = + decodeFrameLength(pendingFrameBytes, &headerSize, &payloadSize); if (!completeLength) { if (headerSize == SIZE_MAX || pendingFrameBytes.size() >= 6) { pendingFrameBytes.clear(); @@ -108,11 +116,16 @@ void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { } if (pendingFrameBytes.size() < headerSize + payloadSize) return; - DebugMessage message{static_cast(pendingFrameBytes[0]), {}}; - message.payload.assign(pendingFrameBytes.begin() + static_cast(headerSize), - pendingFrameBytes.begin() + static_cast(headerSize + payloadSize)); - pendingFrameBytes.erase(pendingFrameBytes.begin(), - pendingFrameBytes.begin() + static_cast(headerSize + payloadSize)); + DebugMessage message{static_cast(pendingFrameBytes[0]), + {}}; + message.payload.assign( + pendingFrameBytes.begin() + static_cast(headerSize), + pendingFrameBytes.begin() + + static_cast(headerSize + payloadSize)); + pendingFrameBytes.erase( + pendingFrameBytes.begin(), + pendingFrameBytes.begin() + + static_cast(headerSize + payloadSize)); pushMessage(std::move(message)); } } @@ -151,14 +164,16 @@ bool Debugger::sendNotification(const debug_NotificationType type, if (payloadSize != 0) { const size_t offset = frame.size(); frame.resize(offset + payloadSize); - pb_ostream_t stream = pb_ostream_from_buffer(frame.data() + offset, payloadSize); + pb_ostream_t stream = + pb_ostream_from_buffer(frame.data() + offset, payloadSize); if (!pb_encode(&stream, fields, payload)) return false; } return channel->writeBytes(frame.data(), frame.size()) == static_cast(frame.size()); } -void Debugger::sendOperationResult(const debug_Command command, const bool success) const { +void Debugger::sendOperationResult(const debug_Command command, + const bool success) const { debug_OperationResult result = debug_OperationResult_init_zero; result.command = command; result.success = success; @@ -216,7 +231,8 @@ bool collectBytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { return out->empty() || pb_read(stream, out->data(), out->size()); } -[[maybe_unused]] bool collectWords(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +[[maybe_unused]] bool collectWords(pb_istream_t *stream, + const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { uint32_t value = 0; @@ -235,14 +251,19 @@ bool collectVarints(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { uint64_t value = 0; - if (!pb_decode_varint(stream, &value) || value > UINT32_MAX) return false; + if (!pb_decode_varint(stream, &value) || value > UINT32_MAX) + return false; out->push_back(static_cast(value)); } return true; } -struct DecodedCallbackEntry { std::string topic; std::vector indexes; }; -bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +struct DecodedCallbackEntry { + std::string topic; + std::vector indexes; +}; +bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, + void **arg) { auto *entries = static_cast *>(*arg); debug_CallbackEntry entry = debug_CallbackEntry_init_zero; std::vector topic; @@ -251,14 +272,17 @@ bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, void entry.table_indexes.funcs.decode = collectVarints; entry.table_indexes.arg = &indexes; if (!pb_decode(stream, debug_CallbackEntry_fields, &entry)) return false; - entries->push_back({std::string(topic.begin(), topic.end()), std::move(indexes)}); + entries->push_back( + {std::string(topic.begin(), topic.end()), std::move(indexes)}); return true; } -std::optional findImportedFunction(Module *m, const std::string &name) { +std::optional findImportedFunction(Module *m, + const std::string &name) { for (uint32_t index = 0; index < m->import_count; ++index) { if (m->functions[index].import_field != nullptr && - name == m->functions[index].import_field) return index; + name == m->functions[index].import_field) + return index; } return std::nullopt; } @@ -273,23 +297,50 @@ bool collectValues(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { bool valueFromProto(const debug_Value &from, StackValue *to) { switch (from.which_data) { - case debug_Value_i32_bits_tag: to->value_type = I32; to->value.uint32 = from.data.i32_bits; return true; - case debug_Value_i64_bits_tag: to->value_type = I64; to->value.uint64 = from.data.i64_bits; return true; - case debug_Value_f32_bits_tag: to->value_type = F32; to->value.uint32 = from.data.f32_bits; return true; - case debug_Value_f64_bits_tag: to->value_type = F64; to->value.uint64 = from.data.f64_bits; return true; - default: return false; + case debug_Value_i32_bits_tag: + to->value_type = I32; + to->value.uint32 = from.data.i32_bits; + return true; + case debug_Value_i64_bits_tag: + to->value_type = I64; + to->value.uint64 = from.data.i64_bits; + return true; + case debug_Value_f32_bits_tag: + to->value_type = F32; + to->value.uint32 = from.data.f32_bits; + return true; + case debug_Value_f64_bits_tag: + to->value_type = F64; + to->value.uint64 = from.data.f64_bits; + return true; + default: + return false; } } -[[maybe_unused]] void valueToProto(const StackValue &from, const uint32_t index, debug_Value *to) { +[[maybe_unused]] void valueToProto(const StackValue &from, const uint32_t index, + debug_Value *to) { *to = debug_Value_init_zero; to->index = index; switch (from.value_type) { - case I32: to->which_data = debug_Value_i32_bits_tag; to->data.i32_bits = from.value.uint32; break; - case I64: to->which_data = debug_Value_i64_bits_tag; to->data.i64_bits = from.value.uint64; break; - case F32: to->which_data = debug_Value_f32_bits_tag; to->data.f32_bits = from.value.uint32; break; - case F64: to->which_data = debug_Value_f64_bits_tag; to->data.f64_bits = from.value.uint64; break; - default: break; + case I32: + to->which_data = debug_Value_i32_bits_tag; + to->data.i32_bits = from.value.uint32; + break; + case I64: + to->which_data = debug_Value_i64_bits_tag; + to->data.i64_bits = from.value.uint64; + break; + case F32: + to->which_data = debug_Value_f32_bits_tag; + to->data.f32_bits = from.value.uint32; + break; + case F64: + to->which_data = debug_Value_f64_bits_tag; + to->data.f64_bits = from.value.uint64; + break; + default: + break; } } @@ -303,7 +354,10 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); }; const auto requireEmpty = [&message, &malformed]() { - if (!message->payload.empty()) { malformed(); return false; } + if (!message->payload.empty()) { + malformed(); + return false; + } return true; }; @@ -321,7 +375,8 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_PAUSE: if (!requireEmpty()) break; pauseRuntime(m); - if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); + if (snapshotPolicy == SnapshotPolicy::checkpointing) + checkpoint(m, true); sendNotification(debug_NotificationType_NOTIFICATION_PAUSED); break; case debug_Command_COMMAND_STEP: @@ -335,21 +390,29 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_ADD_BREAKPOINT: case debug_Command_COMMAND_REMOVE_BREAKPOINT: { debug_Breakpoint breakpoint = debug_Breakpoint_init_zero; - if (!decodePayload(message->payload, debug_Breakpoint_fields, &breakpoint) || - !breakpoint.has_location || breakpoint.location.module_index != 0 || - !isToPhysicalAddrPossible(breakpoint.location.program_counter, m)) { + if (!decodePayload(message->payload, debug_Breakpoint_fields, + &breakpoint) || + !breakpoint.has_location || + breakpoint.location.module_index != 0 || + !isToPhysicalAddrPossible(breakpoint.location.program_counter, + m)) { malformed(); break; } - uint8_t *address = toPhysicalAddress(breakpoint.location.program_counter, m); - if (message->type == debug_Command_COMMAND_ADD_BREAKPOINT) addBreakpoint(address); - else deleteBreakpoint(address); + uint8_t *address = + toPhysicalAddress(breakpoint.location.program_counter, m); + if (message->type == debug_Command_COMMAND_ADD_BREAKPOINT) + addBreakpoint(address); + else + deleteBreakpoint(address); sendOperationResult(message->type, true); break; } case debug_Command_COMMAND_CONTINUE_FOR: { debug_ContinueFor request = debug_ContinueFor_init_zero; - if (!decodePayload(message->payload, debug_ContinueFor_fields, &request) || request.count == 0) { + if (!decodePayload(message->payload, debug_ContinueFor_fields, + &request) || + request.count == 0) { malformed(); break; } @@ -375,7 +438,11 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { break; case debug_Command_COMMAND_DUMP_EVENTS: { debug_Range range = debug_Range_init_zero; - if (!decodePayload(message->payload, debug_Range_fields, &range) || range.end < range.start) { malformed(); break; } + if (!decodePayload(message->payload, debug_Range_fields, &range) || + range.end < range.start) { + malformed(); + break; + } dumpEvents(range.start, range.end - range.start); break; } @@ -387,31 +454,56 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_UPDATE_GLOBAL: case debug_Command_COMMAND_UPDATE_STACK: { debug_ValueUpdate update = debug_ValueUpdate_init_zero; - if (!decodePayload(message->payload, debug_ValueUpdate_fields, &update) || !update.has_value) { malformed(); break; } + if (!decodePayload(message->payload, debug_ValueUpdate_fields, + &update) || + !update.has_value) { + malformed(); + break; + } StackValue *value = nullptr; if (message->type == debug_Command_COMMAND_UPDATE_LOCAL) { ExecutionContext *ectx = m->warduino->execution_context; - if (ectx->fp + static_cast(update.index) > ectx->sp) { malformed(); break; } + if (ectx->fp + static_cast(update.index) > ectx->sp) { + malformed(); + break; + } value = &ectx->stack[ectx->fp + update.index]; } else if (message->type == debug_Command_COMMAND_UPDATE_GLOBAL) { - if (update.index >= m->global_count) { malformed(); break; } + if (update.index >= m->global_count) { + malformed(); + break; + } value = m->globals[update.index]->value; } else { ExecutionContext *ectx = m->warduino->execution_context; - if (update.index > static_cast(ectx->sp)) { malformed(); break; } + if (update.index > static_cast(ectx->sp)) { + malformed(); + break; + } value = &ectx->stack[update.index]; } const bool success = valueFromProto(update.value, value); - if (!success) malformed(); else sendOperationResult(message->type, true); + if (!success) + malformed(); + else + sendOperationResult(message->type, true); break; } case debug_Command_COMMAND_UPDATE_MODULE: { debug_ModuleUpdate update = debug_ModuleUpdate_init_zero; std::vector wasm; setDecodeCallback(&update.wasm, &wasm); - if (!decodePayload(message->payload, debug_ModuleUpdate_fields, &update) || wasm.empty()) { malformed(); break; } + if (!decodePayload(message->payload, debug_ModuleUpdate_fields, + &update) || + wasm.empty()) { + malformed(); + break; + } auto *copy = static_cast(malloc(wasm.size())); - if (copy == nullptr) { sendOperationResult(message->type, false); break; } + if (copy == nullptr) { + sendOperationResult(message->type, false); + break; + } memcpy(copy, wasm.data(), wasm.size()); m->warduino->update_module(m, copy, wasm.size()); sendOperationResult(message->type, true); @@ -421,13 +513,18 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { debug_Function update = debug_Function_init_zero; std::vector instructions; setDecodeCallback(&update.instructions, &instructions); - if (!decodePayload(message->payload, debug_Function_fields, &update) || - update.function_index >= m->function_count || instructions.empty() || - instructions.back() != 0x0b) { malformed(); break; } + if (!decodePayload(message->payload, debug_Function_fields, + &update) || + update.function_index >= m->function_count || + instructions.empty() || instructions.back() != 0x0b) { + malformed(); + break; + } functionBodies[update.function_index] = std::move(instructions); Block &function = m->functions[update.function_index]; function.start_ptr = functionBodies[update.function_index].data(); - function.end_ptr = function.start_ptr + functionBodies[update.function_index].size() - 1; + function.end_ptr = function.start_ptr + + functionBodies[update.function_index].size() - 1; function.br_ptr = function.end_ptr; sendOperationResult(message->type, true); break; @@ -437,20 +534,32 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { std::vector entries; mapping.entries.funcs.decode = collectCallbackEntries; mapping.entries.arg = &entries; - if (!decodePayload(message->payload, debug_CallbackMapping_fields, &mapping)) { malformed(); break; } + if (!decodePayload(message->payload, debug_CallbackMapping_fields, + &mapping)) { + malformed(); + break; + } CallbackHandler::clear_callbacks(); for (const auto &entry : entries) { - for (uint32_t index : entry.indexes) CallbackHandler::add_callback(Callback(m, entry.topic, index)); + for (uint32_t index : entry.indexes) + CallbackHandler::add_callback( + Callback(m, entry.topic, index)); } sendOperationResult(message->type, true); break; } case debug_Command_COMMAND_SET_SNAPSHOT_POLICY: { - debug_SnapshotPolicyConfig config = debug_SnapshotPolicyConfig_init_zero; + debug_SnapshotPolicyConfig config = + debug_SnapshotPolicyConfig_init_zero; std::vector selectedState; setDecodeCallback(&config.selected_state, &selectedState); - if (!decodePayload(message->payload, debug_SnapshotPolicyConfig_fields, &config) || - config.policy > debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING) { malformed(); break; } + if (!decodePayload(message->payload, + debug_SnapshotPolicyConfig_fields, &config) || + config.policy > + debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING) { + malformed(); + break; + } snapshotPolicy = static_cast(config.policy); checkpointInterval = config.interval == 0 ? 1 : config.interval; min_return_values = config.minimum_return_count; @@ -458,11 +567,17 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { checkpoint_state = nullptr; checkpoint_state_size = static_cast(selectedState.size()); if (!selectedState.empty()) { - checkpoint_state = static_cast(malloc(selectedState.size())); - if (checkpoint_state == nullptr) { sendOperationResult(message->type, false); break; } - memcpy(checkpoint_state, selectedState.data(), selectedState.size()); + checkpoint_state = + static_cast(malloc(selectedState.size())); + if (checkpoint_state == nullptr) { + sendOperationResult(message->type, false); + break; + } + memcpy(checkpoint_state, selectedState.data(), + selectedState.size()); } - if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); + if (snapshotPolicy == SnapshotPolicy::checkpointing) + checkpoint(m, true); sendOperationResult(message->type, true); break; } @@ -474,12 +589,25 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { setDecodeCallback(&request.primitive_name, &nameBytes); request.argument_words.funcs.decode = collectWords; request.argument_words.arg = &words; - if (!decodePayload(message->payload, debug_Override_fields, &request)) { malformed(); break; } - const auto fidx = findImportedFunction(m, std::string(nameBytes.begin(), nameBytes.end())); - if (!fidx || words.size() != m->functions[*fidx].type->param_count) { sendOperationResult(message->type, false); break; } + if (!decodePayload(message->payload, debug_Override_fields, + &request)) { + malformed(); + break; + } + const auto fidx = findImportedFunction( + m, std::string(nameBytes.begin(), nameBytes.end())); + if (!fidx || + words.size() != m->functions[*fidx].type->param_count) { + sendOperationResult(message->type, false); + break; + } words.push_back(*fidx); - if (message->type == debug_Command_COMMAND_SET_OVERRIDE) overrides[words] = request.result; - else if (overrides.erase(words) == 0) { sendOperationResult(message->type, false); break; } + if (message->type == debug_Command_COMMAND_SET_OVERRIDE) + overrides[words] = request.result; + else if (overrides.erase(words) == 0) { + sendOperationResult(message->type, false); + break; + } sendOperationResult(message->type, true); break; } @@ -487,26 +615,42 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { debug_Inspect inspectRequest = debug_Inspect_init_zero; std::vector ignored; setDecodeCallback(&inspectRequest.state, &ignored); - if (!decodePayload(message->payload, debug_Inspect_fields, &inspectRequest)) { malformed(); break; } + if (!decodePayload(message->payload, debug_Inspect_fields, + &inspectRequest)) { + malformed(); + break; + } snapshot(m); break; } case debug_Command_COMMAND_LOAD_SNAPSHOT: { debug_Snapshot state = debug_Snapshot_init_zero; - if (!decodePayload(message->payload, debug_Snapshot_fields, &state) || - !isToPhysicalAddrPossible(state.program_counter, m)) { malformed(); break; } + if (!decodePayload(message->payload, debug_Snapshot_fields, + &state) || + !isToPhysicalAddrPossible(state.program_counter, m)) { + malformed(); + break; + } pauseRuntime(m); - m->warduino->execution_context->pc_ptr = toPhysicalAddress(state.program_counter, m); + m->warduino->execution_context->pc_ptr = + toPhysicalAddress(state.program_counter, m); sendOperationResult(message->type, true); break; } case debug_Command_COMMAND_ADD_PROXY: case debug_Command_COMMAND_REMOVE_PROXY: { debug_FunctionRef reference = debug_FunctionRef_init_zero; - if (!decodePayload(message->payload, debug_FunctionRef_fields, &reference) || - supervisor == nullptr || reference.function_index >= m->function_count) { sendOperationResult(message->type, false); break; } - if (message->type == debug_Command_COMMAND_ADD_PROXY) supervisor->registerProxiedCall(reference.function_index); - else supervisor->unregisterProxiedCall(reference.function_index); + if (!decodePayload(message->payload, debug_FunctionRef_fields, + &reference) || + supervisor == nullptr || + reference.function_index >= m->function_count) { + sendOperationResult(message->type, false); + break; + } + if (message->type == debug_Command_COMMAND_ADD_PROXY) + supervisor->registerProxiedCall(reference.function_index); + else + supervisor->unregisterProxiedCall(reference.function_index); sendOperationResult(message->type, true); break; } @@ -516,26 +660,47 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { std::vector values; call.arguments.funcs.decode = collectValues; call.arguments.arg = &values; - if (!decodePayload(message->payload, debug_RemoteFunctionCall_fields, &call) || + if (!decodePayload(message->payload, + debug_RemoteFunctionCall_fields, &call) || call.function_index >= m->function_count || - values.size() != m->functions[call.function_index].type->param_count) { malformed(); break; } + values.size() != + m->functions[call.function_index].type->param_count) { + malformed(); + break; + } auto *arguments = new StackValue[values.size()]; bool valid = true; - for (size_t index = 0; index < values.size(); ++index) valid &= valueFromProto(values[index], &arguments[index]); - if (!valid) { delete[] arguments; malformed(); break; } + for (size_t index = 0; index < values.size(); ++index) + valid &= valueFromProto(values[index], &arguments[index]); + if (!valid) { + delete[] arguments; + malformed(); + break; + } if (message->type == debug_Command_COMMAND_PROXY_CALL) { - if (proxy == nullptr) { delete[] arguments; sendOperationResult(message->type, false); break; } - proxy->pushRFC(m, new RFC(call.function_index, m->functions[call.function_index].type, arguments)); + if (proxy == nullptr) { + delete[] arguments; + sendOperationResult(message->type, false); + break; + } + proxy->pushRFC( + m, + new RFC(call.function_index, + m->functions[call.function_index].type, arguments)); break; } const RunningState current = m->warduino->program_state; m->warduino->program_state = WARDUINOrun; - m->warduino->invoke(m, call.function_index, static_cast(values.size()), arguments); + m->warduino->invoke(m, call.function_index, + static_cast(values.size()), + arguments); m->warduino->program_state = current; - debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; + debug_RemoteFunctionResult result = + debug_RemoteFunctionResult_init_zero; result.success = true; - sendNotification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, - debug_RemoteFunctionResult_fields, &result); + sendNotification( + debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, + debug_RemoteFunctionResult_fields, &result); break; } case debug_Command_COMMAND_PROXIFY: @@ -545,7 +710,8 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { break; case debug_Command_COMMAND_POP_EVENT: if (!requireEmpty()) break; - sendOperationResult(message->type, CallbackHandler::resolve_event(true)); + sendOperationResult(message->type, + CallbackHandler::resolve_event(true)); break; case debug_Command_COMMAND_PUSH_EVENT: { debug_Event event = debug_Event_init_zero; @@ -553,9 +719,14 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { std::vector payload; setDecodeCallback(&event.topic, &topic); setDecodeCallback(&event.payload, &payload); - if (!decodePayload(message->payload, debug_Event_fields, &event) || topic.empty()) { malformed(); break; } - CallbackHandler::push_event(std::string(topic.begin(), topic.end()), - reinterpret_cast(payload.data()), payload.size()); + if (!decodePayload(message->payload, debug_Event_fields, &event) || + topic.empty()) { + malformed(); + break; + } + CallbackHandler::push_event( + std::string(topic.begin(), topic.end()), + reinterpret_cast(payload.data()), payload.size()); notifyPushedEvent(); break; } @@ -571,7 +742,8 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } // Private methods -void Debugger::printValue(const StackValue *, const uint32_t, const bool) const {} +void Debugger::printValue(const StackValue *, const uint32_t, + const bool) const {} uint8_t *Debugger::findOpcode(Module *m, const Block *block) { const auto find = @@ -689,7 +861,9 @@ void Debugger::dumpEvents(long start, long size) const { events.total_count = static_cast(CallbackHandler::event_count()); events.has_range = true; events.range.start = start < 0 ? 0 : static_cast(start); - events.range.end = size < 0 ? events.range.start : events.range.start + static_cast(size); + events.range.end = size < 0 + ? events.range.start + : events.range.start + static_cast(size); sendNotification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, debug_EventsQueue_fields, &events); } @@ -808,12 +982,24 @@ void Debugger::snapshot(Module *m) const { state.program_counter = toVirtualAddress(ectx->pc_ptr, m); state.heap_used = m->warduino->get_heap_used(); switch (m->warduino->program_state) { - case WARDUINOrun: state.state = debug_State_STATE_WARDUINO_RUN; break; - case WARDUINOpause: state.state = debug_State_STATE_WARDUINO_PAUSE; break; - case WARDUINOstep: state.state = debug_State_STATE_WARDUINO_STEP; break; - case PROXYrun: state.state = debug_State_STATE_PROXY_RUN; break; - case PROXYhalt: state.state = debug_State_STATE_PROXY_HALT; break; - default: state.state = debug_State_STATE_WARDUINO_PAUSE; break; + case WARDUINOrun: + state.state = debug_State_STATE_WARDUINO_RUN; + break; + case WARDUINOpause: + state.state = debug_State_STATE_WARDUINO_PAUSE; + break; + case WARDUINOstep: + state.state = debug_State_STATE_WARDUINO_STEP; + break; + case PROXYrun: + state.state = debug_State_STATE_PROXY_RUN; + break; + case PROXYhalt: + state.state = debug_State_STATE_PROXY_HALT; + break; + default: + state.state = debug_State_STATE_WARDUINO_PAUSE; + break; } sendNotification(debug_NotificationType_NOTIFICATION_SNAPSHOT, debug_Snapshot_fields, &state); @@ -924,9 +1110,9 @@ void Debugger::handleSnapshotPolicy(Module *m) { } void Debugger::checkpoint(Module *, const bool force, const bool full) { - if (instructions_executed == 0 && !force) { - return; - } + if (instructions_executed == 0 && !force) { + return; + } debug_Checkpoint notification = debug_Checkpoint_init_zero; notification.instruction_count = instructions_executed; @@ -1502,9 +1688,8 @@ void Debugger::addOverride(Module *m, uint8_t *interruptData) { const std::optional fidx = resolve_imported_function(m, primitive_name); if (!fidx) { - debug( - "Cannot override the result for unknown function \"%s\".\n", - primitive_name.c_str()); + debug("Cannot override the result for unknown function \"%s\".\n", + primitive_name.c_str()); debug("ack%x;0\n", interruptUnsetOverridePinValue); return; } @@ -1527,7 +1712,7 @@ void Debugger::removeOverride(Module *m, uint8_t *interruptData) { resolve_imported_function(m, primitive_name); if (!fidx) { debug("Cannot remove override for unknown function \"%s\".\n", - primitive_name.c_str()); + primitive_name.c_str()); debug("ack%x;0\n", interruptUnsetOverridePinValue); return; } diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index fc73fe889..3fe7945fb 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -1,9 +1,9 @@ #pragma once #include -#include #include #include +#include #include #include #include // std::queue @@ -16,7 +16,6 @@ #include "../Edward/proxy_supervisor.h" #include "../Threading/warduino-thread.h" #include "../Utils/sockets.h" - #include "nanopb/debug.pb.h" #include "nanopb/pb_decode.h" #include "nanopb/pb_encode.h" diff --git a/src/Utils/sockets.cpp b/src/Utils/sockets.cpp index 1ed7b4ba4..d3847b0ac 100644 --- a/src/Utils/sockets.cpp +++ b/src/Utils/sockets.cpp @@ -6,13 +6,12 @@ #include #endif +#include #include #include -#include #include #include #include -#include #ifdef WIFI_ENABLED // Socket Debugger Interface @@ -103,7 +102,8 @@ ssize_t Sink::writeBytes(const uint8_t *data, const size_t size) { if (data == nullptr && size != 0) return -1; size_t offset = 0; while (offset < size) { - const size_t written = fwrite(data + offset, 1, size - offset, this->outStream); + const size_t written = + fwrite(data + offset, 1, size - offset, this->outStream); if (written == 0) return -1; offset += written; } @@ -132,11 +132,15 @@ int FileDescriptorChannel::write(const char *fmt, ...) { return written; } -ssize_t FileDescriptorChannel::writeBytes(const uint8_t *data, const size_t size) { +ssize_t FileDescriptorChannel::writeBytes(const uint8_t *data, + const size_t size) { size_t offset = 0; while (offset < size) { const ssize_t written = ::write(this->fd, data + offset, size - offset); - if (written > 0) { offset += static_cast(written); continue; } + if (written > 0) { + offset += static_cast(written); + continue; + } if (written < 0 && errno == EINTR) continue; return -1; } @@ -203,8 +207,12 @@ ssize_t WebSocket::writeBytes(const uint8_t *data, const size_t size) { if (this->socket < 0) return -1; size_t offset = 0; while (offset < size) { - const ssize_t written = ::write(this->socket, data + offset, size - offset); - if (written > 0) { offset += static_cast(written); continue; } + const ssize_t written = + ::write(this->socket, data + offset, size - offset); + if (written > 0) { + offset += static_cast(written); + continue; + } if (written < 0 && errno == EINTR) continue; return -1; } diff --git a/src/Utils/sockets.h b/src/Utils/sockets.h index 377f08307..33002872e 100644 --- a/src/Utils/sockets.h +++ b/src/Utils/sockets.h @@ -2,8 +2,8 @@ #include -#include #include +#include #ifdef __ZEPHYR__ #if IS_ENABLED(CONFIG_WIFI) diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index 5fdebcc81..adce0a2ae 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -88,7 +88,6 @@ bool CallbackHandler::resolve_event(bool force) { CallbackHandler::events->empty()) { if (force) { printf("No events to be processed!\n"); - } return false; } From d0e8103c12a4c8235449b42cd325bbbfc5f2f0c2 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Sat, 29 Aug 2026 22:18:22 +0200 Subject: [PATCH 13/26] Fix nanopb install in CI --- .github/actions/setup-nanopb/action.yml | 17 +++++++++++++++++ .github/workflows/compile.yml | 4 ++-- .github/workflows/test.yml | 18 ++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 .github/actions/setup-nanopb/action.yml diff --git a/.github/actions/setup-nanopb/action.yml b/.github/actions/setup-nanopb/action.yml new file mode 100644 index 000000000..5c9d72763 --- /dev/null +++ b/.github/actions/setup-nanopb/action.yml @@ -0,0 +1,17 @@ +name: Set up nanopb +description: Install nanopb and expose its protoc generator on PATH. + +runs: + using: composite + steps: + - name: Install nanopb + shell: bash + run: | + brew install nanopb + echo "$(brew --prefix nanopb)/bin" >> "$GITHUB_PATH" + + - name: Verify protobuf tools + shell: bash + run: | + command -v protoc + command -v protoc-gen-nanopb diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index e56735011..5a5fef245 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -40,8 +40,8 @@ jobs: if: ${{ ! startsWith(matrix.os, 'macos') }} uses: Homebrew/actions/setup-homebrew@main - - name: Install protoc/nanopb - run: brew install nanopb + - name: Set up nanopb + uses: ./.github/actions/setup-nanopb - name: Build WARDuino CLI run: cmake .. -D BUILD_EMULATOR=ON ; cmake --build . diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1cd0a388..f5907ec0f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,12 @@ jobs: - name: Create build folder run: mkdir build-unit-tests + - name: Set up Homebrew + uses: Homebrew/actions/setup-homebrew@main + + - name: Set up nanopb + uses: ./.github/actions/setup-nanopb + - name: Build unit tests run: cmake .. -D BUILD_UNITTEST=ON ; cmake --build . working-directory: build-unit-tests @@ -47,8 +53,8 @@ jobs: - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@main - - name: Install protoc/nanopb - run: brew install nanopb + - name: Set up nanopb + uses: ./.github/actions/setup-nanopb - name: Build warduino cli run: | @@ -108,8 +114,8 @@ jobs: - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@main - - name: Install protoc/nanopb - run: brew install nanopb + - name: Set up nanopb + uses: ./.github/actions/setup-nanopb - name: Build warduino cli run: | @@ -167,8 +173,8 @@ jobs: - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@main - - name: Install protoc/nanopb - run: brew install nanopb + - name: Set up nanopb + uses: ./.github/actions/setup-nanopb - name: Build warduino cli run: | From 7ce0c54b1d3cbb13efdc28ddaaa2635b70b4a1af Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Sat, 29 Aug 2026 23:05:01 +0200 Subject: [PATCH 14/26] Use vendored protobuf files + improve CI --- .github/actions/setup-nanopb/action.yml | 17 - .github/workflows/compile.yml | 13 +- .github/workflows/test.yml | 45 +- CMakeLists.txt | 27 +- justfile | 10 + platforms/ESP-IDF/CMakeLists.txt | 6 +- platforms/Zephyr/CMakeLists.txt | 6 +- src/Debug/debugger.cpp | 2 +- src/Debug/nanopb/debug.pb.h | 809 ++++++++++++++++++++++++ 9 files changed, 848 insertions(+), 87 deletions(-) delete mode 100644 .github/actions/setup-nanopb/action.yml create mode 100644 src/Debug/nanopb/debug.pb.h diff --git a/.github/actions/setup-nanopb/action.yml b/.github/actions/setup-nanopb/action.yml deleted file mode 100644 index 5c9d72763..000000000 --- a/.github/actions/setup-nanopb/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Set up nanopb -description: Install nanopb and expose its protoc generator on PATH. - -runs: - using: composite - steps: - - name: Install nanopb - shell: bash - run: | - brew install nanopb - echo "$(brew --prefix nanopb)/bin" >> "$GITHUB_PATH" - - - name: Verify protobuf tools - shell: bash - run: | - command -v protoc - command -v protoc-gen-nanopb diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 5a5fef245..a5c19e66a 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -33,19 +33,8 @@ jobs: with: submodules: 'recursive' - - name: Create build folder - run: mkdir build-emu - - - name: Set up Homebrew - if: ${{ ! startsWith(matrix.os, 'macos') }} - uses: Homebrew/actions/setup-homebrew@main - - - name: Set up nanopb - uses: ./.github/actions/setup-nanopb - - name: Build WARDuino CLI - run: cmake .. -D BUILD_EMULATOR=ON ; cmake --build . - working-directory: build-emu + uses: ./.github/actions/build-wdcli compile-with-arduino: name: (Arduino) Compile on ${{matrix.board.platform-name}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f5907ec0f..cd8062a66 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,12 +20,6 @@ jobs: - name: Create build folder run: mkdir build-unit-tests - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@main - - - name: Set up nanopb - uses: ./.github/actions/setup-nanopb - - name: Build unit tests run: cmake .. -D BUILD_UNITTEST=ON ; cmake --build . working-directory: build-unit-tests @@ -50,17 +44,8 @@ jobs: with: node-version: 20 - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@main - - - name: Set up nanopb - uses: ./.github/actions/setup-nanopb - - - name: Build warduino cli - run: | - cmake . -D BUILD_EMULATOR=ON - cmake --build . - echo "EMULATOR=$(realpath ./wdcli)" >> $GITHUB_ENV + - name: Build WARDuino CLI + uses: ./.github/actions/build-wdcli - name: Get WABT commit ID working-directory: lib/wabt @@ -111,17 +96,8 @@ jobs: with: node-version: 20 - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@main - - - name: Set up nanopb - uses: ./.github/actions/setup-nanopb - - - name: Build warduino cli - run: | - cmake . -D BUILD_EMULATOR=ON - cmake --build . - echo "EMULATOR=$(realpath ./wdcli)" >> $GITHUB_ENV + - name: Build WARDuino CLI + uses: ./.github/actions/build-wdcli - name: Get WABT commit ID working-directory: lib/wabt @@ -170,17 +146,8 @@ jobs: with: node-version: 20 - - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@main - - - name: Set up nanopb - uses: ./.github/actions/setup-nanopb - - - name: Build warduino cli - run: | - cmake . -D BUILD_EMULATOR=ON - cmake --build . - echo "EMULATOR=$(realpath ./wdcli)" >> $GITHUB_ENV + - name: Build WARDuino CLI + uses: ./.github/actions/build-wdcli - name: Get WABT commit ID working-directory: lib/wabt diff --git a/CMakeLists.txt b/CMakeLists.txt index 360b33bff..94a195126 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,22 +36,17 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") set(WARDUINO_VERSION_STRING "${PROJECT_VERSION}") configure_file(src/config.h.in include/warduino/config.h) -# Both host targets use one generated nanopb schema target. -if (BUILD_EMULATOR OR BUILD_UNITTEST) - list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib) - include(FetchContent) - FetchContent_Declare( - nanopb - GIT_REPOSITORY https://github.com/nanopb/nanopb.git - GIT_TAG 0.4.9.1 - GIT_SHALLOW TRUE - ) - FetchContent_MakeAvailable(nanopb) - set(NANOPB_SRC_ROOT_FOLDER ${nanopb_SOURCE_DIR}) - find_program(NANOPB_GENERATOR_PLUGIN NAMES protoc-gen-nanopb REQUIRED) - find_package(Nanopb REQUIRED) - nanopb_generate_cpp(TARGET proto src/Debug/debug.proto) -endif () +# Use the checked-in nanopb runtime and schema output on every platform. +add_library(proto STATIC + src/Debug/nanopb/debug.pb.c + src/Debug/nanopb/pb_common.c + src/Debug/nanopb/pb_decode.c + src/Debug/nanopb/pb_encode.c +) +target_include_directories(proto PUBLIC + ${PROJECT_SOURCE_DIR}/src/Debug + ${PROJECT_SOURCE_DIR}/src/Debug/nanopb +) # Build the emulator version of WARDuino if (BUILD_EMULATOR) diff --git a/justfile b/justfile index 166c9bd04..7612ff23b 100644 --- a/justfile +++ b/justfile @@ -164,6 +164,16 @@ all: WABT="../../lib/wabt/build/" npm run tests:all +## Nanopb + +[group('codegen')] +[doc('Regenerate vendored nanopb sources from the debugger schema')] +generate-nanopb: _nanopb + mkdir -p src/Debug/nanopb + protoc -I src/Debug --nanopb_out=src/Debug/nanopb src/Debug/debug.proto + perl -0pi -e 's{#include }{#include "pb.h"}' src/Debug/nanopb/debug.pb.h + + ## QoL / Maintenance [group('maintenance')] diff --git a/platforms/ESP-IDF/CMakeLists.txt b/platforms/ESP-IDF/CMakeLists.txt index 12abbd25e..c0e251a01 100644 --- a/platforms/ESP-IDF/CMakeLists.txt +++ b/platforms/ESP-IDF/CMakeLists.txt @@ -1,5 +1,9 @@ set(SOURCE_FILES ../../src/Debug/debugger.cpp + ../../src/Debug/nanopb/debug.pb.c + ../../src/Debug/nanopb/pb_common.c + ../../src/Debug/nanopb/pb_decode.c + ../../src/Debug/nanopb/pb_encode.c ../../src/Interpreter/instructions.cpp ../../src/Interpreter/interpreter.cpp ../../src/Memory/mem.cpp @@ -15,7 +19,7 @@ set(SOURCE_FILES ../../src/WARDuino/WARDuino.cpp ) -idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS ../../lib/json/single_include/ REQUIRES driver) +idf_component_register(SRCS "main.cpp" ${SOURCE_FILES} INCLUDE_DIRS ../../lib/json/single_include/ ../../src/Debug/nanopb REQUIRES driver) add_definitions(-DINFO=0) add_definitions(-DDEBUG=0) diff --git a/platforms/Zephyr/CMakeLists.txt b/platforms/Zephyr/CMakeLists.txt index 23951adbe..f47956678 100644 --- a/platforms/Zephyr/CMakeLists.txt +++ b/platforms/Zephyr/CMakeLists.txt @@ -27,6 +27,10 @@ target_sources(app PRIVATE ../../src/Utils/macros.cpp ../../src/Utils/sockets.cpp ../../src/Debug/debugger.cpp + ../../src/Debug/nanopb/debug.pb.c + ../../src/Debug/nanopb/pb_common.c + ../../src/Debug/nanopb/pb_decode.c + ../../src/Debug/nanopb/pb_encode.c ../../src/Edward/proxy.cpp ../../src/Edward/proxy_supervisor.cpp ../../src/Edward/RFC.cpp @@ -38,4 +42,4 @@ add_definitions(-DDEBUG=0) add_definitions(-DTRACE=0) add_definitions(-DWARN=0) -include_directories(../../lib/json/single_include/) +include_directories(../../lib/json/single_include/ ../../src/Debug/nanopb) diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 43f04eeab..96dc8c21e 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -1109,7 +1109,7 @@ void Debugger::handleSnapshotPolicy(Module *m) { } } -void Debugger::checkpoint(Module *, const bool force, const bool full) { +void Debugger::checkpoint(Module *, const bool force, const bool) { if (instructions_executed == 0 && !force) { return; } diff --git a/src/Debug/nanopb/debug.pb.h b/src/Debug/nanopb/debug.pb.h new file mode 100644 index 000000000..cf2bed6be --- /dev/null +++ b/src/Debug/nanopb/debug.pb.h @@ -0,0 +1,809 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_DEBUG_DEBUG_PB_H_INCLUDED +#define PB_DEBUG_DEBUG_PB_H_INCLUDED +#include "pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* Frontend -> WARDuino. + The receiver selects the payload schema from the command byte. */ +typedef enum _debug_Command { + debug_Command_COMMAND_RUN = 0, /* no payload */ + debug_Command_COMMAND_HALT = 1, /* no payload */ + debug_Command_COMMAND_PAUSE = 2, /* no payload */ + debug_Command_COMMAND_STEP = 3, /* no payload */ + debug_Command_COMMAND_STEP_OVER = 4, /* no payload */ + debug_Command_COMMAND_ADD_BREAKPOINT = 5, /* Breakpoint */ + debug_Command_COMMAND_REMOVE_BREAKPOINT = 6, /* Breakpoint */ + debug_Command_COMMAND_DUMP = 7, /* no payload */ + debug_Command_COMMAND_DUMP_LOCALS = 8, /* no payload */ + debug_Command_COMMAND_SNAPSHOT = 9, /* no payload */ + debug_Command_COMMAND_DUMP_EVENTS = 10, /* Range */ + debug_Command_COMMAND_DUMP_CALLBACKS = 11, /* no payload */ + debug_Command_COMMAND_UPDATE_FUNCTION = 12, /* Function */ + debug_Command_COMMAND_UPDATE_LOCAL = 13, /* ValueUpdate */ + debug_Command_COMMAND_UPDATE_CALLBACKS = 14, /* CallbackMapping */ + debug_Command_COMMAND_UPDATE_MODULE = 26, /* ModuleUpdate */ + debug_Command_COMMAND_UPDATE_GLOBAL = 27, /* ValueUpdate */ + debug_Command_COMMAND_UPDATE_STACK = 28, /* ValueUpdate */ + debug_Command_COMMAND_LOAD_SNAPSHOT = 15, /* Snapshot */ + debug_Command_COMMAND_PROXIFY = 16, /* no payload */ + debug_Command_COMMAND_ADD_PROXY = 17, /* FunctionRef */ + debug_Command_COMMAND_REMOVE_PROXY = 18, /* FunctionRef */ + debug_Command_COMMAND_PROXY_CALL = 19, /* RemoteFunctionCall */ + debug_Command_COMMAND_POP_EVENT = 20, /* no payload */ + debug_Command_COMMAND_PUSH_EVENT = 21, /* Event */ + debug_Command_COMMAND_CONTINUE_FOR = 22, /* ContinueFor */ + debug_Command_COMMAND_INSPECT = 23, /* Inspect */ + debug_Command_COMMAND_RESET = 24, /* no payload */ + debug_Command_COMMAND_INVOKE = 25, /* RemoteFunctionCall */ + debug_Command_COMMAND_SET_SNAPSHOT_POLICY = 29, /* SnapshotPolicyConfig */ + debug_Command_COMMAND_SET_OVERRIDE = 30, /* Override */ + debug_Command_COMMAND_REMOVE_OVERRIDE = 31 /* Override */ +} debug_Command; + +/* WARDuino -> frontend. + The receiver selects the payload schema from the notification byte. */ +typedef enum _debug_NotificationType { + debug_NotificationType_NOTIFICATION_CONTINUED = 0, /* no payload */ + debug_NotificationType_NOTIFICATION_HALTED = 1, /* no payload */ + debug_NotificationType_NOTIFICATION_PAUSED = 2, /* no payload */ + debug_NotificationType_NOTIFICATION_STEPPED = 3, /* no payload */ + debug_NotificationType_NOTIFICATION_HIT_BREAKPOINT = 4, /* HitBreakpoint */ + debug_NotificationType_NOTIFICATION_NEW_EVENT = 5, /* NewEvent (zero-length payload) */ + debug_NotificationType_NOTIFICATION_FUNCTION_DUMP = 6, /* Function */ + debug_NotificationType_NOTIFICATION_LOCALS_DUMP = 7, /* Locals */ + debug_NotificationType_NOTIFICATION_SNAPSHOT = 8, /* Snapshot */ + debug_NotificationType_NOTIFICATION_EVENTS_DUMP = 9, /* EventsQueue */ + debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP = 10, /* CallbackMapping */ + debug_NotificationType_NOTIFICATION_CHANGE_AFFECTED = 11, /* no payload */ + debug_NotificationType_NOTIFICATION_MALFORMED = 12, /* no payload */ + debug_NotificationType_NOTIFICATION_UNKNOWN_COMMAND = 13, /* no payload */ + debug_NotificationType_NOTIFICATION_OPERATION_RESULT = 14, /* OperationResult */ + debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT = 15, /* RemoteFunctionResult */ + debug_NotificationType_NOTIFICATION_CHECKPOINT = 16 /* Checkpoint */ +} debug_NotificationType; + +typedef enum _debug_State { + debug_State_STATE_WARDUINO_RUN = 0, + debug_State_STATE_WARDUINO_PAUSE = 1, + debug_State_STATE_WARDUINO_STEP = 2, + debug_State_STATE_PROXY_RUN = 3, + debug_State_STATE_PROXY_HALT = 4 +} debug_State; + +typedef enum _debug_SnapshotPolicy { + debug_SnapshotPolicy_SNAPSHOT_POLICY_NONE = 0, + debug_SnapshotPolicy_SNAPSHOT_POLICY_EVERY_INSTRUCTION = 1, + debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING = 2 +} debug_SnapshotPolicy; + +/* Struct definitions */ +/* A virtual program address. */ +typedef struct _debug_CodeLocation { + uint32_t module_index; + uint32_t program_counter; +} debug_CodeLocation; + +typedef struct _debug_Breakpoint { + bool has_location; + debug_CodeLocation location; +} debug_Breakpoint; + +typedef struct _debug_HitBreakpoint { + bool has_location; + debug_CodeLocation location; +} debug_HitBreakpoint; + +/* The notification type carries all information for this event. The empty + message exists for host-side reflection, but no protobuf bytes are sent. */ +typedef struct _debug_NewEvent { + char dummy_field; +} debug_NewEvent; + +typedef struct _debug_ContinueFor { + uint32_t count; +} debug_ContinueFor; + +/* Execution-state selectors understood by the VM. Keeping them as bytes lets + the protocol add selectors without changing this schema. */ +typedef struct _debug_Inspect { + pb_callback_t state; +} debug_Inspect; + +typedef struct _debug_FunctionRef { + uint32_t function_index; +} debug_FunctionRef; + +typedef struct _debug_RemoteFunctionCall { + uint32_t function_index; + pb_callback_t arguments; +} debug_RemoteFunctionCall; + +typedef struct _debug_CallstackEntry { + uint32_t type; + uint32_t function_index; + uint32_t stack_pointer; + uint32_t frame_pointer; + uint32_t start; + uint32_t return_address; +} debug_CallstackEntry; + +typedef struct _debug_Locals { + pb_callback_t values; +} debug_Locals; + +/* The oneof tag is the value type, so a separate type enum and a decimal + string are unnecessary. Fixed-width fields preserve WebAssembly bits and + are fast to construct on the MCU. */ +typedef struct _debug_Value { + pb_size_t which_data; + union { + uint32_t i32_bits; + uint64_t i64_bits; + uint32_t f32_bits; + uint64_t f64_bits; + pb_callback_t raw; + } data; + uint32_t index; +} debug_Value; + +typedef struct _debug_ValueUpdate { + uint32_t index; + bool has_value; + debug_Value value; +} debug_ValueUpdate; + +typedef struct _debug_CallbackMapping { + pb_callback_t entries; +} debug_CallbackMapping; + +typedef struct _debug_CallbackEntry { + pb_callback_t topic; + pb_callback_t table_indexes; +} debug_CallbackEntry; + +typedef struct _debug_Event { + pb_callback_t topic; + pb_callback_t payload; +} debug_Event; + +typedef struct _debug_Range { + uint32_t start; + uint32_t end; +} debug_Range; + +typedef struct _debug_Function { + uint32_t function_index; + bool has_range; + debug_Range range; + bool has_locals; + debug_Locals locals; + pb_callback_t instructions; +} debug_Function; + +typedef struct _debug_EventsQueue { + /* Total events in the queue; this can exceed the returned slice length. */ + uint32_t total_count; + pb_callback_t events; + bool has_range; + debug_Range range; +} debug_EventsQueue; + +typedef struct _debug_ModuleUpdate { + pb_callback_t wasm; +} debug_ModuleUpdate; + +typedef struct _debug_IndexedValues { + pb_callback_t values; +} debug_IndexedValues; + +typedef struct _debug_SnapshotPolicyConfig { + debug_SnapshotPolicy policy; + uint32_t interval; + uint32_t minimum_return_count; + pb_callback_t selected_state; +} debug_SnapshotPolicyConfig; + +typedef struct _debug_Override { + pb_callback_t primitive_name; + pb_callback_t argument_words; + uint32_t result; +} debug_Override; + +typedef struct _debug_OperationResult { + debug_Command command; + bool success; +} debug_OperationResult; + +typedef struct _debug_RemoteFunctionResult { + bool success; + pb_callback_t results; + pb_callback_t error; +} debug_RemoteFunctionResult; + +typedef struct _debug_TableState { + uint32_t initial; + uint32_t maximum; + pb_callback_t entries; +} debug_TableState; + +typedef struct _debug_MemoryState { + uint32_t initial; + uint32_t maximum; + uint32_t pages; + pb_callback_t bytes; +} debug_MemoryState; + +typedef struct _debug_Snapshot { + uint32_t program_counter; + debug_State state; + pb_callback_t breakpoints; + pb_callback_t functions; + pb_callback_t callstack; + bool has_locals; + debug_Locals locals; + bool has_queue; + debug_EventsQueue queue; + bool has_callbacks; + debug_CallbackMapping callbacks; + pb_callback_t globals; + pb_callback_t stack; + bool has_table; + debug_TableState table; + bool has_memory; + debug_MemoryState memory; + pb_callback_t branch_table; + pb_callback_t io; + pb_callback_t overrides; + uint32_t heap_used; +} debug_Snapshot; + +typedef struct _debug_Checkpoint { + uint32_t instruction_count; + bool has_primitive_call; + uint32_t primitive_function_index; + pb_callback_t arguments; + pb_callback_t results; + bool has_snapshot; + debug_Snapshot snapshot; +} debug_Checkpoint; + +typedef struct _debug_IOState { + pb_callback_t key; + bool output; + int32_t value; +} debug_IOState; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _debug_Command_MIN debug_Command_COMMAND_RUN +#define _debug_Command_MAX debug_Command_COMMAND_REMOVE_OVERRIDE +#define _debug_Command_ARRAYSIZE ((debug_Command)(debug_Command_COMMAND_REMOVE_OVERRIDE+1)) + +#define _debug_NotificationType_MIN debug_NotificationType_NOTIFICATION_CONTINUED +#define _debug_NotificationType_MAX debug_NotificationType_NOTIFICATION_CHECKPOINT +#define _debug_NotificationType_ARRAYSIZE ((debug_NotificationType)(debug_NotificationType_NOTIFICATION_CHECKPOINT+1)) + +#define _debug_State_MIN debug_State_STATE_WARDUINO_RUN +#define _debug_State_MAX debug_State_STATE_PROXY_HALT +#define _debug_State_ARRAYSIZE ((debug_State)(debug_State_STATE_PROXY_HALT+1)) + +#define _debug_SnapshotPolicy_MIN debug_SnapshotPolicy_SNAPSHOT_POLICY_NONE +#define _debug_SnapshotPolicy_MAX debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING +#define _debug_SnapshotPolicy_ARRAYSIZE ((debug_SnapshotPolicy)(debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING+1)) + + + + + + + + + +#define debug_Snapshot_state_ENUMTYPE debug_State + + + + + + + + + + + + + +#define debug_SnapshotPolicyConfig_policy_ENUMTYPE debug_SnapshotPolicy + + +#define debug_OperationResult_command_ENUMTYPE debug_Command + + + + + + + +/* Initializer values for message structs */ +#define debug_CodeLocation_init_default {0, 0} +#define debug_Breakpoint_init_default {false, debug_CodeLocation_init_default} +#define debug_HitBreakpoint_init_default {false, debug_CodeLocation_init_default} +#define debug_NewEvent_init_default {0} +#define debug_ContinueFor_init_default {0} +#define debug_Inspect_init_default {{{NULL}, NULL}} +#define debug_FunctionRef_init_default {0} +#define debug_ValueUpdate_init_default {0, false, debug_Value_init_default} +#define debug_Snapshot_init_default {0, _debug_State_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_Locals_init_default, false, debug_EventsQueue_init_default, false, debug_CallbackMapping_init_default, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_TableState_init_default, false, debug_MemoryState_init_default, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define debug_Function_init_default {0, false, debug_Range_init_default, false, debug_Locals_init_default, {{NULL}, NULL}} +#define debug_RemoteFunctionCall_init_default {0, {{NULL}, NULL}} +#define debug_CallstackEntry_init_default {0, 0, 0, 0, 0, 0} +#define debug_Locals_init_default {{{NULL}, NULL}} +#define debug_Value_init_default {0, {0}, 0} +#define debug_CallbackMapping_init_default {{{NULL}, NULL}} +#define debug_CallbackEntry_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define debug_EventsQueue_init_default {0, {{NULL}, NULL}, false, debug_Range_init_default} +#define debug_Event_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define debug_Range_init_default {0, 0} +#define debug_ModuleUpdate_init_default {{{NULL}, NULL}} +#define debug_IndexedValues_init_default {{{NULL}, NULL}} +#define debug_SnapshotPolicyConfig_init_default {_debug_SnapshotPolicy_MIN, 0, 0, {{NULL}, NULL}} +#define debug_Override_init_default {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define debug_OperationResult_init_default {_debug_Command_MIN, 0} +#define debug_RemoteFunctionResult_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}} +#define debug_Checkpoint_init_default {0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_Snapshot_init_default} +#define debug_TableState_init_default {0, 0, {{NULL}, NULL}} +#define debug_MemoryState_init_default {0, 0, 0, {{NULL}, NULL}} +#define debug_IOState_init_default {{{NULL}, NULL}, 0, 0} +#define debug_CodeLocation_init_zero {0, 0} +#define debug_Breakpoint_init_zero {false, debug_CodeLocation_init_zero} +#define debug_HitBreakpoint_init_zero {false, debug_CodeLocation_init_zero} +#define debug_NewEvent_init_zero {0} +#define debug_ContinueFor_init_zero {0} +#define debug_Inspect_init_zero {{{NULL}, NULL}} +#define debug_FunctionRef_init_zero {0} +#define debug_ValueUpdate_init_zero {0, false, debug_Value_init_zero} +#define debug_Snapshot_init_zero {0, _debug_State_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_Locals_init_zero, false, debug_EventsQueue_init_zero, false, debug_CallbackMapping_init_zero, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_TableState_init_zero, false, debug_MemoryState_init_zero, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define debug_Function_init_zero {0, false, debug_Range_init_zero, false, debug_Locals_init_zero, {{NULL}, NULL}} +#define debug_RemoteFunctionCall_init_zero {0, {{NULL}, NULL}} +#define debug_CallstackEntry_init_zero {0, 0, 0, 0, 0, 0} +#define debug_Locals_init_zero {{{NULL}, NULL}} +#define debug_Value_init_zero {0, {0}, 0} +#define debug_CallbackMapping_init_zero {{{NULL}, NULL}} +#define debug_CallbackEntry_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define debug_EventsQueue_init_zero {0, {{NULL}, NULL}, false, debug_Range_init_zero} +#define debug_Event_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define debug_Range_init_zero {0, 0} +#define debug_ModuleUpdate_init_zero {{{NULL}, NULL}} +#define debug_IndexedValues_init_zero {{{NULL}, NULL}} +#define debug_SnapshotPolicyConfig_init_zero {_debug_SnapshotPolicy_MIN, 0, 0, {{NULL}, NULL}} +#define debug_Override_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define debug_OperationResult_init_zero {_debug_Command_MIN, 0} +#define debug_RemoteFunctionResult_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}} +#define debug_Checkpoint_init_zero {0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, false, debug_Snapshot_init_zero} +#define debug_TableState_init_zero {0, 0, {{NULL}, NULL}} +#define debug_MemoryState_init_zero {0, 0, 0, {{NULL}, NULL}} +#define debug_IOState_init_zero {{{NULL}, NULL}, 0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define debug_CodeLocation_module_index_tag 1 +#define debug_CodeLocation_program_counter_tag 2 +#define debug_Breakpoint_location_tag 1 +#define debug_HitBreakpoint_location_tag 1 +#define debug_ContinueFor_count_tag 1 +#define debug_Inspect_state_tag 1 +#define debug_FunctionRef_function_index_tag 1 +#define debug_RemoteFunctionCall_function_index_tag 1 +#define debug_RemoteFunctionCall_arguments_tag 2 +#define debug_CallstackEntry_type_tag 1 +#define debug_CallstackEntry_function_index_tag 2 +#define debug_CallstackEntry_stack_pointer_tag 3 +#define debug_CallstackEntry_frame_pointer_tag 4 +#define debug_CallstackEntry_start_tag 5 +#define debug_CallstackEntry_return_address_tag 6 +#define debug_Locals_values_tag 1 +#define debug_Value_i32_bits_tag 1 +#define debug_Value_i64_bits_tag 2 +#define debug_Value_f32_bits_tag 3 +#define debug_Value_f64_bits_tag 4 +#define debug_Value_raw_tag 5 +#define debug_Value_index_tag 6 +#define debug_ValueUpdate_index_tag 1 +#define debug_ValueUpdate_value_tag 2 +#define debug_CallbackMapping_entries_tag 1 +#define debug_CallbackEntry_topic_tag 1 +#define debug_CallbackEntry_table_indexes_tag 2 +#define debug_Event_topic_tag 1 +#define debug_Event_payload_tag 2 +#define debug_Range_start_tag 1 +#define debug_Range_end_tag 2 +#define debug_Function_function_index_tag 1 +#define debug_Function_range_tag 2 +#define debug_Function_locals_tag 3 +#define debug_Function_instructions_tag 4 +#define debug_EventsQueue_total_count_tag 1 +#define debug_EventsQueue_events_tag 2 +#define debug_EventsQueue_range_tag 3 +#define debug_ModuleUpdate_wasm_tag 1 +#define debug_IndexedValues_values_tag 1 +#define debug_SnapshotPolicyConfig_policy_tag 1 +#define debug_SnapshotPolicyConfig_interval_tag 2 +#define debug_SnapshotPolicyConfig_minimum_return_count_tag 3 +#define debug_SnapshotPolicyConfig_selected_state_tag 4 +#define debug_Override_primitive_name_tag 1 +#define debug_Override_argument_words_tag 2 +#define debug_Override_result_tag 3 +#define debug_OperationResult_command_tag 1 +#define debug_OperationResult_success_tag 2 +#define debug_RemoteFunctionResult_success_tag 1 +#define debug_RemoteFunctionResult_results_tag 2 +#define debug_RemoteFunctionResult_error_tag 3 +#define debug_TableState_initial_tag 1 +#define debug_TableState_maximum_tag 2 +#define debug_TableState_entries_tag 3 +#define debug_MemoryState_initial_tag 1 +#define debug_MemoryState_maximum_tag 2 +#define debug_MemoryState_pages_tag 3 +#define debug_MemoryState_bytes_tag 4 +#define debug_Snapshot_program_counter_tag 1 +#define debug_Snapshot_state_tag 2 +#define debug_Snapshot_breakpoints_tag 3 +#define debug_Snapshot_functions_tag 4 +#define debug_Snapshot_callstack_tag 5 +#define debug_Snapshot_locals_tag 6 +#define debug_Snapshot_queue_tag 7 +#define debug_Snapshot_callbacks_tag 8 +#define debug_Snapshot_globals_tag 9 +#define debug_Snapshot_stack_tag 10 +#define debug_Snapshot_table_tag 11 +#define debug_Snapshot_memory_tag 12 +#define debug_Snapshot_branch_table_tag 13 +#define debug_Snapshot_io_tag 14 +#define debug_Snapshot_overrides_tag 15 +#define debug_Snapshot_heap_used_tag 16 +#define debug_Checkpoint_instruction_count_tag 1 +#define debug_Checkpoint_has_primitive_call_tag 2 +#define debug_Checkpoint_primitive_function_index_tag 3 +#define debug_Checkpoint_arguments_tag 4 +#define debug_Checkpoint_results_tag 5 +#define debug_Checkpoint_snapshot_tag 6 +#define debug_IOState_key_tag 1 +#define debug_IOState_output_tag 2 +#define debug_IOState_value_tag 3 + +/* Struct field encoding specification for nanopb */ +#define debug_CodeLocation_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, module_index, 1) \ +X(a, STATIC, SINGULAR, UINT32, program_counter, 2) +#define debug_CodeLocation_CALLBACK NULL +#define debug_CodeLocation_DEFAULT NULL + +#define debug_Breakpoint_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, location, 1) +#define debug_Breakpoint_CALLBACK NULL +#define debug_Breakpoint_DEFAULT NULL +#define debug_Breakpoint_location_MSGTYPE debug_CodeLocation + +#define debug_HitBreakpoint_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, location, 1) +#define debug_HitBreakpoint_CALLBACK NULL +#define debug_HitBreakpoint_DEFAULT NULL +#define debug_HitBreakpoint_location_MSGTYPE debug_CodeLocation + +#define debug_NewEvent_FIELDLIST(X, a) \ + +#define debug_NewEvent_CALLBACK NULL +#define debug_NewEvent_DEFAULT NULL + +#define debug_ContinueFor_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, count, 1) +#define debug_ContinueFor_CALLBACK NULL +#define debug_ContinueFor_DEFAULT NULL + +#define debug_Inspect_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, state, 1) +#define debug_Inspect_CALLBACK pb_default_field_callback +#define debug_Inspect_DEFAULT NULL + +#define debug_FunctionRef_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, function_index, 1) +#define debug_FunctionRef_CALLBACK NULL +#define debug_FunctionRef_DEFAULT NULL + +#define debug_ValueUpdate_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, index, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, value, 2) +#define debug_ValueUpdate_CALLBACK NULL +#define debug_ValueUpdate_DEFAULT NULL +#define debug_ValueUpdate_value_MSGTYPE debug_Value + +#define debug_Snapshot_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, program_counter, 1) \ +X(a, STATIC, SINGULAR, UENUM, state, 2) \ +X(a, CALLBACK, REPEATED, UINT32, breakpoints, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, functions, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, callstack, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, locals, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, queue, 7) \ +X(a, STATIC, OPTIONAL, MESSAGE, callbacks, 8) \ +X(a, CALLBACK, REPEATED, MESSAGE, globals, 9) \ +X(a, CALLBACK, REPEATED, MESSAGE, stack, 10) \ +X(a, STATIC, OPTIONAL, MESSAGE, table, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, memory, 12) \ +X(a, CALLBACK, REPEATED, UINT32, branch_table, 13) \ +X(a, CALLBACK, REPEATED, MESSAGE, io, 14) \ +X(a, CALLBACK, REPEATED, MESSAGE, overrides, 15) \ +X(a, STATIC, SINGULAR, UINT32, heap_used, 16) +#define debug_Snapshot_CALLBACK pb_default_field_callback +#define debug_Snapshot_DEFAULT NULL +#define debug_Snapshot_functions_MSGTYPE debug_Function +#define debug_Snapshot_callstack_MSGTYPE debug_CallstackEntry +#define debug_Snapshot_locals_MSGTYPE debug_Locals +#define debug_Snapshot_queue_MSGTYPE debug_EventsQueue +#define debug_Snapshot_callbacks_MSGTYPE debug_CallbackMapping +#define debug_Snapshot_globals_MSGTYPE debug_Value +#define debug_Snapshot_stack_MSGTYPE debug_Value +#define debug_Snapshot_table_MSGTYPE debug_TableState +#define debug_Snapshot_memory_MSGTYPE debug_MemoryState +#define debug_Snapshot_io_MSGTYPE debug_IOState +#define debug_Snapshot_overrides_MSGTYPE debug_Override + +#define debug_Function_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, function_index, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, range, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, locals, 3) \ +X(a, CALLBACK, SINGULAR, BYTES, instructions, 4) +#define debug_Function_CALLBACK pb_default_field_callback +#define debug_Function_DEFAULT NULL +#define debug_Function_range_MSGTYPE debug_Range +#define debug_Function_locals_MSGTYPE debug_Locals + +#define debug_RemoteFunctionCall_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, function_index, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, arguments, 2) +#define debug_RemoteFunctionCall_CALLBACK pb_default_field_callback +#define debug_RemoteFunctionCall_DEFAULT NULL +#define debug_RemoteFunctionCall_arguments_MSGTYPE debug_Value + +#define debug_CallstackEntry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, type, 1) \ +X(a, STATIC, SINGULAR, UINT32, function_index, 2) \ +X(a, STATIC, SINGULAR, UINT32, stack_pointer, 3) \ +X(a, STATIC, SINGULAR, UINT32, frame_pointer, 4) \ +X(a, STATIC, SINGULAR, UINT32, start, 5) \ +X(a, STATIC, SINGULAR, UINT32, return_address, 6) +#define debug_CallstackEntry_CALLBACK NULL +#define debug_CallstackEntry_DEFAULT NULL + +#define debug_Locals_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, values, 1) +#define debug_Locals_CALLBACK pb_default_field_callback +#define debug_Locals_DEFAULT NULL +#define debug_Locals_values_MSGTYPE debug_Value + +#define debug_Value_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, FIXED32, (data,i32_bits,data.i32_bits), 1) \ +X(a, STATIC, ONEOF, FIXED64, (data,i64_bits,data.i64_bits), 2) \ +X(a, STATIC, ONEOF, FIXED32, (data,f32_bits,data.f32_bits), 3) \ +X(a, STATIC, ONEOF, FIXED64, (data,f64_bits,data.f64_bits), 4) \ +X(a, CALLBACK, ONEOF, BYTES, (data,raw,data.raw), 5) \ +X(a, STATIC, SINGULAR, UINT32, index, 6) +#define debug_Value_CALLBACK pb_default_field_callback +#define debug_Value_DEFAULT NULL + +#define debug_CallbackMapping_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, entries, 1) +#define debug_CallbackMapping_CALLBACK pb_default_field_callback +#define debug_CallbackMapping_DEFAULT NULL +#define debug_CallbackMapping_entries_MSGTYPE debug_CallbackEntry + +#define debug_CallbackEntry_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, topic, 1) \ +X(a, CALLBACK, REPEATED, UINT32, table_indexes, 2) +#define debug_CallbackEntry_CALLBACK pb_default_field_callback +#define debug_CallbackEntry_DEFAULT NULL + +#define debug_EventsQueue_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, total_count, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, events, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, range, 3) +#define debug_EventsQueue_CALLBACK pb_default_field_callback +#define debug_EventsQueue_DEFAULT NULL +#define debug_EventsQueue_events_MSGTYPE debug_Event +#define debug_EventsQueue_range_MSGTYPE debug_Range + +#define debug_Event_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, topic, 1) \ +X(a, CALLBACK, SINGULAR, BYTES, payload, 2) +#define debug_Event_CALLBACK pb_default_field_callback +#define debug_Event_DEFAULT NULL + +#define debug_Range_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, start, 1) \ +X(a, STATIC, SINGULAR, UINT32, end, 2) +#define debug_Range_CALLBACK NULL +#define debug_Range_DEFAULT NULL + +#define debug_ModuleUpdate_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, wasm, 1) +#define debug_ModuleUpdate_CALLBACK pb_default_field_callback +#define debug_ModuleUpdate_DEFAULT NULL + +#define debug_IndexedValues_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, values, 1) +#define debug_IndexedValues_CALLBACK pb_default_field_callback +#define debug_IndexedValues_DEFAULT NULL +#define debug_IndexedValues_values_MSGTYPE debug_Value + +#define debug_SnapshotPolicyConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, policy, 1) \ +X(a, STATIC, SINGULAR, UINT32, interval, 2) \ +X(a, STATIC, SINGULAR, UINT32, minimum_return_count, 3) \ +X(a, CALLBACK, SINGULAR, BYTES, selected_state, 4) +#define debug_SnapshotPolicyConfig_CALLBACK pb_default_field_callback +#define debug_SnapshotPolicyConfig_DEFAULT NULL + +#define debug_Override_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, primitive_name, 1) \ +X(a, CALLBACK, REPEATED, FIXED32, argument_words, 2) \ +X(a, STATIC, SINGULAR, FIXED32, result, 3) +#define debug_Override_CALLBACK pb_default_field_callback +#define debug_Override_DEFAULT NULL + +#define debug_OperationResult_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, command, 1) \ +X(a, STATIC, SINGULAR, BOOL, success, 2) +#define debug_OperationResult_CALLBACK NULL +#define debug_OperationResult_DEFAULT NULL + +#define debug_RemoteFunctionResult_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, success, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, results, 2) \ +X(a, CALLBACK, SINGULAR, BYTES, error, 3) +#define debug_RemoteFunctionResult_CALLBACK pb_default_field_callback +#define debug_RemoteFunctionResult_DEFAULT NULL +#define debug_RemoteFunctionResult_results_MSGTYPE debug_Value + +#define debug_Checkpoint_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, instruction_count, 1) \ +X(a, STATIC, SINGULAR, BOOL, has_primitive_call, 2) \ +X(a, STATIC, SINGULAR, UINT32, primitive_function_index, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, arguments, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, results, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, snapshot, 6) +#define debug_Checkpoint_CALLBACK pb_default_field_callback +#define debug_Checkpoint_DEFAULT NULL +#define debug_Checkpoint_arguments_MSGTYPE debug_Value +#define debug_Checkpoint_results_MSGTYPE debug_Value +#define debug_Checkpoint_snapshot_MSGTYPE debug_Snapshot + +#define debug_TableState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, initial, 1) \ +X(a, STATIC, SINGULAR, UINT32, maximum, 2) \ +X(a, CALLBACK, REPEATED, UINT32, entries, 3) +#define debug_TableState_CALLBACK pb_default_field_callback +#define debug_TableState_DEFAULT NULL + +#define debug_MemoryState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, initial, 1) \ +X(a, STATIC, SINGULAR, UINT32, maximum, 2) \ +X(a, STATIC, SINGULAR, UINT32, pages, 3) \ +X(a, CALLBACK, SINGULAR, BYTES, bytes, 4) +#define debug_MemoryState_CALLBACK pb_default_field_callback +#define debug_MemoryState_DEFAULT NULL + +#define debug_IOState_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, STATIC, SINGULAR, BOOL, output, 2) \ +X(a, STATIC, SINGULAR, SINT32, value, 3) +#define debug_IOState_CALLBACK pb_default_field_callback +#define debug_IOState_DEFAULT NULL + +extern const pb_msgdesc_t debug_CodeLocation_msg; +extern const pb_msgdesc_t debug_Breakpoint_msg; +extern const pb_msgdesc_t debug_HitBreakpoint_msg; +extern const pb_msgdesc_t debug_NewEvent_msg; +extern const pb_msgdesc_t debug_ContinueFor_msg; +extern const pb_msgdesc_t debug_Inspect_msg; +extern const pb_msgdesc_t debug_FunctionRef_msg; +extern const pb_msgdesc_t debug_ValueUpdate_msg; +extern const pb_msgdesc_t debug_Snapshot_msg; +extern const pb_msgdesc_t debug_Function_msg; +extern const pb_msgdesc_t debug_RemoteFunctionCall_msg; +extern const pb_msgdesc_t debug_CallstackEntry_msg; +extern const pb_msgdesc_t debug_Locals_msg; +extern const pb_msgdesc_t debug_Value_msg; +extern const pb_msgdesc_t debug_CallbackMapping_msg; +extern const pb_msgdesc_t debug_CallbackEntry_msg; +extern const pb_msgdesc_t debug_EventsQueue_msg; +extern const pb_msgdesc_t debug_Event_msg; +extern const pb_msgdesc_t debug_Range_msg; +extern const pb_msgdesc_t debug_ModuleUpdate_msg; +extern const pb_msgdesc_t debug_IndexedValues_msg; +extern const pb_msgdesc_t debug_SnapshotPolicyConfig_msg; +extern const pb_msgdesc_t debug_Override_msg; +extern const pb_msgdesc_t debug_OperationResult_msg; +extern const pb_msgdesc_t debug_RemoteFunctionResult_msg; +extern const pb_msgdesc_t debug_Checkpoint_msg; +extern const pb_msgdesc_t debug_TableState_msg; +extern const pb_msgdesc_t debug_MemoryState_msg; +extern const pb_msgdesc_t debug_IOState_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define debug_CodeLocation_fields &debug_CodeLocation_msg +#define debug_Breakpoint_fields &debug_Breakpoint_msg +#define debug_HitBreakpoint_fields &debug_HitBreakpoint_msg +#define debug_NewEvent_fields &debug_NewEvent_msg +#define debug_ContinueFor_fields &debug_ContinueFor_msg +#define debug_Inspect_fields &debug_Inspect_msg +#define debug_FunctionRef_fields &debug_FunctionRef_msg +#define debug_ValueUpdate_fields &debug_ValueUpdate_msg +#define debug_Snapshot_fields &debug_Snapshot_msg +#define debug_Function_fields &debug_Function_msg +#define debug_RemoteFunctionCall_fields &debug_RemoteFunctionCall_msg +#define debug_CallstackEntry_fields &debug_CallstackEntry_msg +#define debug_Locals_fields &debug_Locals_msg +#define debug_Value_fields &debug_Value_msg +#define debug_CallbackMapping_fields &debug_CallbackMapping_msg +#define debug_CallbackEntry_fields &debug_CallbackEntry_msg +#define debug_EventsQueue_fields &debug_EventsQueue_msg +#define debug_Event_fields &debug_Event_msg +#define debug_Range_fields &debug_Range_msg +#define debug_ModuleUpdate_fields &debug_ModuleUpdate_msg +#define debug_IndexedValues_fields &debug_IndexedValues_msg +#define debug_SnapshotPolicyConfig_fields &debug_SnapshotPolicyConfig_msg +#define debug_Override_fields &debug_Override_msg +#define debug_OperationResult_fields &debug_OperationResult_msg +#define debug_RemoteFunctionResult_fields &debug_RemoteFunctionResult_msg +#define debug_Checkpoint_fields &debug_Checkpoint_msg +#define debug_TableState_fields &debug_TableState_msg +#define debug_MemoryState_fields &debug_MemoryState_msg +#define debug_IOState_fields &debug_IOState_msg + +/* Maximum encoded size of messages (where known) */ +/* debug_Inspect_size depends on runtime parameters */ +/* debug_ValueUpdate_size depends on runtime parameters */ +/* debug_Snapshot_size depends on runtime parameters */ +/* debug_Function_size depends on runtime parameters */ +/* debug_RemoteFunctionCall_size depends on runtime parameters */ +/* debug_Locals_size depends on runtime parameters */ +/* debug_Value_size depends on runtime parameters */ +/* debug_CallbackMapping_size depends on runtime parameters */ +/* debug_CallbackEntry_size depends on runtime parameters */ +/* debug_EventsQueue_size depends on runtime parameters */ +/* debug_Event_size depends on runtime parameters */ +/* debug_ModuleUpdate_size depends on runtime parameters */ +/* debug_IndexedValues_size depends on runtime parameters */ +/* debug_SnapshotPolicyConfig_size depends on runtime parameters */ +/* debug_Override_size depends on runtime parameters */ +/* debug_RemoteFunctionResult_size depends on runtime parameters */ +/* debug_Checkpoint_size depends on runtime parameters */ +/* debug_TableState_size depends on runtime parameters */ +/* debug_MemoryState_size depends on runtime parameters */ +/* debug_IOState_size depends on runtime parameters */ +#define DEBUG_DEBUG_PB_H_MAX_SIZE debug_CallstackEntry_size +#define debug_Breakpoint_size 14 +#define debug_CallstackEntry_size 36 +#define debug_CodeLocation_size 12 +#define debug_ContinueFor_size 6 +#define debug_FunctionRef_size 6 +#define debug_HitBreakpoint_size 14 +#define debug_NewEvent_size 0 +#define debug_OperationResult_size 4 +#define debug_Range_size 12 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif From 7805f0bc87e79c55c151788a279f2ecb13e0fa7a Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Sun, 30 Aug 2026 09:05:43 +0200 Subject: [PATCH 15/26] Add missing files --- .clang-format-ignore | 1 + .github/actions/build-wdcli/action.yml | 25 + .gitignore | 4 + src/Debug/nanopb/debug.pb.c | 104 ++ src/Debug/nanopb/pb.h | 922 +++++++++++++ src/Debug/nanopb/pb_common.c | 388 ++++++ src/Debug/nanopb/pb_common.h | 49 + src/Debug/nanopb/pb_decode.c | 1728 ++++++++++++++++++++++++ src/Debug/nanopb/pb_decode.h | 204 +++ src/Debug/nanopb/pb_encode.c | 1001 ++++++++++++++ src/Debug/nanopb/pb_encode.h | 195 +++ 11 files changed, 4621 insertions(+) create mode 100644 .github/actions/build-wdcli/action.yml create mode 100644 src/Debug/nanopb/debug.pb.c create mode 100644 src/Debug/nanopb/pb.h create mode 100644 src/Debug/nanopb/pb_common.c create mode 100644 src/Debug/nanopb/pb_common.h create mode 100644 src/Debug/nanopb/pb_decode.c create mode 100644 src/Debug/nanopb/pb_decode.h create mode 100644 src/Debug/nanopb/pb_encode.c create mode 100644 src/Debug/nanopb/pb_encode.h diff --git a/.clang-format-ignore b/.clang-format-ignore index d3295e729..44f9b03f4 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1 +1,2 @@ src/Debug/debug.proto +src/Debug/nanopb/** diff --git a/.github/actions/build-wdcli/action.yml b/.github/actions/build-wdcli/action.yml new file mode 100644 index 000000000..baf23bf62 --- /dev/null +++ b/.github/actions/build-wdcli/action.yml @@ -0,0 +1,25 @@ +name: Build WARDuino CLI +description: Configure and build the WARDuino emulator CLI. + +inputs: + build-directory: + description: Directory for CMake build artifacts. + required: false + default: build-emu + +outputs: + wdcli: + description: Absolute path to the built CLI executable. + value: ${{ steps.build.outputs.wdcli }} + +runs: + using: composite + steps: + - id: build + shell: bash + run: | + cmake -S "$GITHUB_WORKSPACE" -B "${{ inputs.build-directory }}" -D BUILD_EMULATOR=ON + cmake --build "${{ inputs.build-directory }}" + wdcli="$(cd "${{ inputs.build-directory }}" && pwd)/wdcli" + echo "wdcli=$wdcli" >> "$GITHUB_OUTPUT" + echo "EMULATOR=$wdcli" >> "$GITHUB_ENV" diff --git a/.gitignore b/.gitignore index f6cfe209a..6cf8eb2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,10 @@ build/ cmake-build-* build* +# local actions +!/.github/actions/build-wdcli/ +!/.github/actions/build-wdcli/action.yml + # CMake CMakeLists.txt.user CMakeCache.txt diff --git a/src/Debug/nanopb/debug.pb.c b/src/Debug/nanopb/debug.pb.c new file mode 100644 index 000000000..d5f6fee3c --- /dev/null +++ b/src/Debug/nanopb/debug.pb.c @@ -0,0 +1,104 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "debug.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(debug_CodeLocation, debug_CodeLocation, AUTO) + + +PB_BIND(debug_Breakpoint, debug_Breakpoint, AUTO) + + +PB_BIND(debug_HitBreakpoint, debug_HitBreakpoint, AUTO) + + +PB_BIND(debug_NewEvent, debug_NewEvent, AUTO) + + +PB_BIND(debug_ContinueFor, debug_ContinueFor, AUTO) + + +PB_BIND(debug_Inspect, debug_Inspect, AUTO) + + +PB_BIND(debug_FunctionRef, debug_FunctionRef, AUTO) + + +PB_BIND(debug_ValueUpdate, debug_ValueUpdate, AUTO) + + +PB_BIND(debug_Snapshot, debug_Snapshot, 2) + + +PB_BIND(debug_Function, debug_Function, AUTO) + + +PB_BIND(debug_RemoteFunctionCall, debug_RemoteFunctionCall, AUTO) + + +PB_BIND(debug_CallstackEntry, debug_CallstackEntry, AUTO) + + +PB_BIND(debug_Locals, debug_Locals, AUTO) + + +PB_BIND(debug_Value, debug_Value, AUTO) + + +PB_BIND(debug_CallbackMapping, debug_CallbackMapping, AUTO) + + +PB_BIND(debug_CallbackEntry, debug_CallbackEntry, AUTO) + + +PB_BIND(debug_EventsQueue, debug_EventsQueue, AUTO) + + +PB_BIND(debug_Event, debug_Event, AUTO) + + +PB_BIND(debug_Range, debug_Range, AUTO) + + +PB_BIND(debug_ModuleUpdate, debug_ModuleUpdate, AUTO) + + +PB_BIND(debug_IndexedValues, debug_IndexedValues, AUTO) + + +PB_BIND(debug_SnapshotPolicyConfig, debug_SnapshotPolicyConfig, AUTO) + + +PB_BIND(debug_Override, debug_Override, AUTO) + + +PB_BIND(debug_OperationResult, debug_OperationResult, AUTO) + + +PB_BIND(debug_RemoteFunctionResult, debug_RemoteFunctionResult, AUTO) + + +PB_BIND(debug_Checkpoint, debug_Checkpoint, 2) + + +PB_BIND(debug_TableState, debug_TableState, AUTO) + + +PB_BIND(debug_MemoryState, debug_MemoryState, AUTO) + + +PB_BIND(debug_IOState, debug_IOState, AUTO) + + + + + + + + + + + diff --git a/src/Debug/nanopb/pb.h b/src/Debug/nanopb/pb.h new file mode 100644 index 000000000..10249bb65 --- /dev/null +++ b/src/Debug/nanopb/pb.h @@ -0,0 +1,922 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-0.4.9.1" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Detect endianness */ +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/src/Debug/nanopb/pb_common.c b/src/Debug/nanopb/pb_common.c new file mode 100644 index 000000000..6aee76b1e --- /dev/null +++ b/src/Debug/nanopb/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/src/Debug/nanopb/pb_common.h b/src/Debug/nanopb/pb_common.h new file mode 100644 index 000000000..58aa90f76 --- /dev/null +++ b/src/Debug/nanopb/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/src/Debug/nanopb/pb_decode.c b/src/Debug/nanopb/pb_decode.c new file mode 100644 index 000000000..b3f96fc76 --- /dev/null +++ b/src/Debug/nanopb/pb_decode.c @@ -0,0 +1,1728 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/src/Debug/nanopb/pb_decode.h b/src/Debug/nanopb/pb_decode.h new file mode 100644 index 000000000..3f392b293 --- /dev/null +++ b/src/Debug/nanopb/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/Debug/nanopb/pb_encode.c b/src/Debug/nanopb/pb_encode.c new file mode 100644 index 000000000..f9034a542 --- /dev/null +++ b/src/Debug/nanopb/pb_encode.c @@ -0,0 +1,1001 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/src/Debug/nanopb/pb_encode.h b/src/Debug/nanopb/pb_encode.h new file mode 100644 index 000000000..6dc089da3 --- /dev/null +++ b/src/Debug/nanopb/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif From de3b02faf268171f3082716e6b8a370beb234a6a Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 08:16:09 +0200 Subject: [PATCH 16/26] Temporary version --- CMakeLists.txt | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94a195126..89034c669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ if (BUILD_ESP) include($ENV{IDF_PATH}/tools/cmake/project.cmake "${PROJECT_BINARY_DIR}/../include") endif (BUILD_ESP) -project(WARDuino VERSION 0.8.1) +project(WARDuino VERSION 0.8.99) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") diff --git a/library.properties b/library.properties index 83c5fb4b7..fd7fd4829 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=WARDuino -version=0.8.1 +version=0.8.99 author=Robbert Gurdeep Singh, Christophe Scholliers , Tom Lauwaerts , Carlos Rojas Castillo , Maarten Steevens , Joel Martin maintainer=Tom Lauwaerts , Maarten Steevens , Christophe Scholliers sentence=A library that enables the use of WebAssembly on Arduino boards with debugging support From 6437e9f02edbc23fcf7038762a6f2d48dfc0579b Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 08:40:51 +0200 Subject: [PATCH 17/26] Fix `invoke` --- src/Debug/debugger.cpp | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 96dc8c21e..461d2e2d0 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -344,6 +344,25 @@ bool valueFromProto(const debug_Value &from, StackValue *to) { } } +bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { + const auto *values = static_cast *>(*arg); + for (size_t index = 0; index < values->size(); ++index) { + debug_Value value = debug_Value_init_zero; + valueToProto((*values)[index], static_cast(index), &value); + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_Value_fields, &value)) { + return false; + } + } + return true; +} + +bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { + const auto *bytes = static_cast *>(*arg); + return pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, bytes->data(), bytes->size()); +} + } // namespace bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { @@ -691,13 +710,25 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } const RunningState current = m->warduino->program_state; m->warduino->program_state = WARDUINOrun; - m->warduino->invoke(m, call.function_index, - static_cast(values.size()), - arguments); + exception[0] = "\0"[0]; + const auto results = m->warduino->invoke( + m, call.function_index, static_cast(values.size()), + arguments); m->warduino->program_state = current; + delete[] arguments; + debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; - result.success = true; + result.success = exception[0] == "\0"[0]; + std::vector error; + if (result.success) { + result.results.funcs.encode = encodeValues; + result.results.arg = const_cast *>(&results); + } else { + error.assign(exception, exception + std::strlen(exception)); + result.error.funcs.encode = encodeBytes; + result.error.arg = &error; + } sendNotification( debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, debug_RemoteFunctionResult_fields, &result); From 00cb7e6dd3797ab5d57df19f4be85e9fde39d1ed Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 11:31:46 +0200 Subject: [PATCH 18/26] Update latch --- tests/latch/package-lock.json | 1041 ++++++++++++++++++++++++++------- tests/latch/package.json | 2 +- tests/latch/src/spec.test.ts | 4 +- 3 files changed, 827 insertions(+), 220 deletions(-) diff --git a/tests/latch/package-lock.json b/tests/latch/package-lock.json index 2b851854d..e8228aaf9 100644 --- a/tests/latch/package-lock.json +++ b/tests/latch/package-lock.json @@ -8,7 +8,7 @@ "name": "warduino-testsuite", "version": "1.0.0", "devDependencies": { - "latch": "file:./latch-0.6.0.tgz", + "latch": "file:./latch-0.7.0.tgz", "mqtt": "^5.15.2", "serialport": "^10.4.0", "typescript": "^4.5.5" @@ -24,6 +24,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bufbuild/protobuf": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.14.0.tgz", + "integrity": "sha512-C3UGsiCwSprE2NKIIFA3hCDlpXTMCAXRZuEVp88L1GY36Y41+rYL5fryE+nOFhp4p4JPQvdV8PQ4DWgHgeTE+w==", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -48,9 +55,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -258,9 +265,9 @@ } }, "node_modules/@thi.ng/checks": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@thi.ng/checks/-/checks-3.9.5.tgz", - "integrity": "sha512-hxSqs6DtE/RDkzWSa3xqbYEwqAhicqKcWRZSvUmQ9I2NEDrR+qBUj6icjXMtS51k+Mp3FRiTQ0FDCI8FbOBBmw==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@thi.ng/checks/-/checks-3.11.1.tgz", + "integrity": "sha512-S64V2nAPA0Y66VwpJdi3fdwv4xP9G7M13nvQWzp6YTERsnusI6FtihbxhENt/DQ/k+iP+aVSsy+LgtqwkFvvag==", "dev": true, "funding": [ { @@ -282,9 +289,9 @@ } }, "node_modules/@thi.ng/errors": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.6.14.tgz", - "integrity": "sha512-dSqLPZh5wOe329Ks2pJqoDmtjSv2g4KpXEP5/IQ5J9qvrEyNrRBCuaKHUKYUltQ1OUHGd9L5hBqGJl65Hlnu+g==", + "version": "2.6.17", + "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.6.17.tgz", + "integrity": "sha512-momw7wo3EXes1wa6Smckr+l4BaJi/h+E67R3furYOBjQpciie1OgoTa43PXPb1POsuWxynkEF8OCnQgreE8twg==", "dev": true, "funding": [ { @@ -306,9 +313,9 @@ } }, "node_modules/@thi.ng/leb128": { - "version": "3.1.90", - "resolved": "https://registry.npmjs.org/@thi.ng/leb128/-/leb128-3.1.90.tgz", - "integrity": "sha512-HR6dQnGPB3P9F+ILHkFw2cyse+aPOEHNS8loUOQ7uapGmZ4ummp+rsg/ZXQ6Bwqa+MZBHODBoeKrrUrARgcDYQ==", + "version": "3.1.94", + "resolved": "https://registry.npmjs.org/@thi.ng/leb128/-/leb128-3.1.94.tgz", + "integrity": "sha512-MNpWK3qnRJ7aUYB0qghtlm0uY4ULBWAPKcezqTMDAX+HVmGqONllhebTjgxmueCljKBqpJMugybnOO99cn5wCg==", "dev": true, "funding": [ { @@ -326,17 +333,17 @@ ], "license": "Apache-2.0", "dependencies": { - "@thi.ng/checks": "^3.9.5", - "@thi.ng/errors": "^2.6.14" + "@thi.ng/checks": "^3.11.1", + "@thi.ng/errors": "^2.6.17" }, "engines": { "node": ">=18" } }, "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", "dev": true, "license": "MIT" }, @@ -362,13 +369,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.4.0.tgz", + "integrity": "sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/readable-stream": { @@ -391,6 +398,13 @@ "@types/node": "*" } }, + "node_modules/@types/yoga-layout": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@types/yoga-layout/-/yoga-layout-1.9.2.tgz", + "integrity": "sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==", + "dev": true, + "license": "MIT" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -405,9 +419,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -430,27 +444,59 @@ "node": ">=0.4.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, "engines": { - "node": ">=6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/arg": { @@ -460,6 +506,29 @@ "dev": true, "license": "MIT" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/auto-bind": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", + "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -494,23 +563,6 @@ "readable-stream": "^4.2.0" } }, - "node_modules/bl/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "dev": true, - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/broker-factory": { "version": "3.1.15", "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.15.tgz", @@ -557,47 +609,105 @@ "license": "MIT" }, "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "license": "MIT", "dependencies": { - "restore-cursor": "^5.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", - "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", + "node_modules/code-excerpt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-3.0.0.tgz", + "integrity": "sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw==", "dev": true, "license": "MIT", + "dependencies": { + "convert-to-spaces": "^1.0.1" + }, "engines": { - "node": ">=18.20" + "node": ">=10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=7.0.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/commist": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", @@ -621,6 +731,31 @@ "typedarray": "^0.0.6" } }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-to-spaces": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz", + "integrity": "sha512-cj09EBuObp9gZNQCzc7hByQyrs6jVGE+o9kSJmeUoj+GiPiJvi5LYqEH/Hmme4+MTLHM+Ejtq+FChpjjEnsPdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -656,6 +791,23 @@ "node": ">=0.3.1" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -690,17 +842,14 @@ "node": ">=18.2.0" } }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/help-me": { @@ -731,6 +880,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -738,40 +897,81 @@ "dev": true, "license": "ISC" }, + "node_modules/ink": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ink/-/ink-3.2.0.tgz", + "integrity": "sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "auto-bind": "4.0.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.1.0", + "code-excerpt": "^3.0.0", + "indent-string": "^4.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.20", + "patch-console": "^1.0.0", + "react-devtools-core": "^4.19.1", + "react-reconciler": "^0.26.2", + "scheduler": "^0.20.2", + "signal-exit": "^3.0.2", + "slice-ansi": "^3.0.0", + "stack-utils": "^2.0.2", + "string-width": "^4.2.2", + "type-fest": "^0.12.0", + "widest-line": "^3.1.0", + "wrap-ansi": "^6.2.0", + "ws": "^7.5.5", + "yoga-layout-prebuilt": "^1.9.6" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/ip-address": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz", - "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", "dev": true, "license": "MIT", "engines": { "node": ">= 12" } }, - "node_modules/is-interactive": { + "node_modules/is-ci": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "ci-info": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "is-ci": "bin.js" } }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/js-sdsl": { @@ -785,44 +985,328 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-with-bigint": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz", - "integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.12.tgz", + "integrity": "sha512-uwbF/wSSuOgC7qqlq27Xp5B6a2MHVug3t0idZdTqu0JnlFvgJuH7ju+KAk/J06C7GfhoYy2gnb9wz2INqcne7w==", "dev": true, "license": "MIT" }, "node_modules/latch": { - "version": "0.6.0", - "resolved": "file:latch-0.6.0.tgz", - "integrity": "sha512-HKyweJBOyp/o3TOXYr0+s3qbMW/Kj5/3s1lILsBzRBv7GxVdQGsvhZSgx6OyJazc0fpp50Kz2Tv81gMHG7oFBQ==", + "version": "0.7.0", + "resolved": "file:latch-0.7.0.tgz", + "integrity": "sha512-zTReKkp8m02jQMr/+HqHdBI4N++m6j4ybOVzPycM/RS03jeq4Xm69lltItWqUYUwARydnxhOXvHJ7nlsxDdHkg==", "dev": true, "dependencies": { + "@bufbuild/protobuf": "^2.14.0", "@thi.ng/leb128": "^3.1.90", - "ansi-colors": "^4.1.3", "ieee754": "^1.2.1", + "ink": "^3.2.0", "json-with-bigint": "^3.5.8", - "ora": "^9.4.0", + "react": "^17.0.2", + "serialport": "^13.0.0", "source-map": "^0.7.6", "ts-node": "^10.9.2", - "tslib": "^2.8.1" + "tslib": "^2.8.1", + "typescript": "^6.0.3" + }, + "bin": { + "latch": "bin/latch.js" } }, - "node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "node_modules/latch/node_modules/@serialport/bindings-cpp": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz", + "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==", "dev": true, + "hasInstallScript": true, "license": "MIT", "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "12.0.0", + "debug": "4.4.0", + "node-addon-api": "8.3.0", + "node-gyp-build": "4.8.4" }, "engines": { - "node": ">=18" + "node": ">=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-byte-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz", + "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-cctalk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz", + "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-delimiter": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz", + "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-inter-byte-timeout": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz", + "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-packet-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz", + "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/latch/node_modules/@serialport/parser-readline": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz", + "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "13.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-ready": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz", + "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-regex": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz", + "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-slip-encoder": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz", + "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/parser-spacepacket": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz", + "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/@serialport/stream": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz", + "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/latch/node_modules/node-addon-api": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", + "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/latch/node_modules/serialport": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz", + "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "13.0.0", + "@serialport/parser-byte-length": "13.0.0", + "@serialport/parser-cctalk": "13.0.0", + "@serialport/parser-delimiter": "13.0.0", + "@serialport/parser-inter-byte-timeout": "13.0.0", + "@serialport/parser-packet-length": "13.0.0", + "@serialport/parser-readline": "13.0.0", + "@serialport/parser-ready": "13.0.0", + "@serialport/parser-regex": "13.0.0", + "@serialport/parser-slip-encoder": "13.0.0", + "@serialport/parser-spacepacket": "13.0.0", + "@serialport/stream": "13.0.0", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/latch/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, "node_modules/lru-cache": { @@ -839,17 +1323,14 @@ "dev": true, "license": "ISC" }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, "node_modules/minimist": { @@ -907,21 +1388,26 @@ "process-nextick-args": "^2.0.1" } }, - "node_modules/mqtt/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "node_modules/mqtt/node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/ms": { @@ -961,43 +1447,40 @@ "js-sdsl": "4.3.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { - "mimic-function": "^5.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=18" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz", - "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==", + "node_modules/patch-console": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-1.0.0.tgz", + "integrity": "sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA==", "dev": true, "license": "MIT", - "dependencies": { - "chalk": "^5.6.2", - "cli-cursor": "^5.0.0", - "cli-spinners": "^3.2.0", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.1.0", - "log-symbols": "^7.0.1", - "stdin-discarder": "^0.3.2", - "string-width": "^8.1.0" - }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, "node_modules/process": { @@ -1017,36 +1500,78 @@ "dev": true, "license": "MIT" }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-devtools-core": { + "version": "4.28.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz", + "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-reconciler": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.26.2.tgz", + "integrity": "sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^17.0.2" + } + }, "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, "license": "MIT", "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/rfdc": { @@ -1077,6 +1602,17 @@ ], "license": "MIT" }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/serialport": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/serialport/-/serialport-10.5.0.tgz", @@ -1106,17 +1642,39 @@ "url": "https://opencollective.com/serialport/donate" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/shell-quote": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/smart-buffer": { @@ -1165,17 +1723,17 @@ "node": ">= 10.x" } }, - "node_modules/stdin-discarder": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", - "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "escape-string-regexp": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=10" } }, "node_modules/string_decoder": { @@ -1189,36 +1747,44 @@ } }, "node_modules/string-width": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", - "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.2.2" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "engines": { + "node": ">=8" } }, "node_modules/ts-node": { @@ -1272,6 +1838,19 @@ "dev": true, "license": "0BSD" }, + "node_modules/type-fest": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", + "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -1294,9 +1873,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT" }, @@ -1314,6 +1893,19 @@ "dev": true, "license": "MIT" }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/worker-factory": { "version": "7.0.50", "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.50.tgz", @@ -1365,18 +1957,33 @@ "worker-factory": "^7.0.50" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ws": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", - "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "dev": true, "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=8.3.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" + "utf-8-validate": "^5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -1397,17 +2004,17 @@ "node": ">=6" } }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "node_modules/yoga-layout-prebuilt": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz", + "integrity": "sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@types/yoga-layout": "1.9.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } } } diff --git a/tests/latch/package.json b/tests/latch/package.json index 9216f3a43..daeb30bc8 100644 --- a/tests/latch/package.json +++ b/tests/latch/package.json @@ -11,7 +11,7 @@ "tests:all": "npm run tests:spec ; npm run tests:integration" }, "devDependencies": { - "latch": "file:./latch-0.6.0.tgz", + "latch": "file:./latch-0.7.0.tgz", "mqtt": "^5.15.2", "serialport": "^10.4.0", "typescript": "^4.5.5" diff --git a/tests/latch/src/spec.test.ts b/tests/latch/src/spec.test.ts index a8e31e853..c70ee0152 100644 --- a/tests/latch/src/spec.test.ts +++ b/tests/latch/src/spec.test.ts @@ -1,4 +1,4 @@ -import {EmulatorSpecification, Framework, invoke, returns, Step, StyleType, TestScenario, WASM} from 'latch'; +import {EmulatorSpecification, Framework, Verbosity, invoke, returns, Step, TestScenario, WASM} from 'latch'; import {readdirSync} from 'fs'; import {basename} from 'path'; import {find, parseArguments, parseAsserts, parseResult} from "./util/spec.util"; @@ -46,7 +46,7 @@ if (TESTFILE.length > 0) { // run tests const framework = Framework.getImplementation(); -framework.reporter.style(StyleType.github); +framework.reporter.verbosity(Verbosity.normal); const spec = framework.suite('Specification test suite for WebAssembly'); spec.testee('emulator [:8500]', new EmulatorSpecification(8500)); From 5575454723a346c53f862b45f054278f13b8ead7 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 11:36:31 +0200 Subject: [PATCH 19/26] Lint --- src/Debug/debugger.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 461d2e2d0..1f59169b4 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -344,7 +344,8 @@ bool valueFromProto(const debug_Value &from, StackValue *to) { } } -bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { +bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { const auto *values = static_cast *>(*arg); for (size_t index = 0; index < values->size(); ++index) { debug_Value value = debug_Value_init_zero; @@ -357,7 +358,8 @@ bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, void * const *a return true; } -bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) { +bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { const auto *bytes = static_cast *>(*arg); return pb_encode_tag_for_field(stream, field) && pb_encode_string(stream, bytes->data(), bytes->size()); @@ -723,7 +725,8 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { std::vector error; if (result.success) { result.results.funcs.encode = encodeValues; - result.results.arg = const_cast *>(&results); + result.results.arg = + const_cast *>(&results); } else { error.assign(exception, exception + std::strlen(exception)); result.error.funcs.encode = encodeBytes; From 268a03944af87bd0a2bcc216940f83b307cbdc9c Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 11:49:34 +0200 Subject: [PATCH 20/26] Add missing files --- tests/latch/latch-0.6.0.tgz | Bin 56983 -> 0 bytes tests/latch/latch-0.7.0.tgz | Bin 0 -> 155658 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/latch/latch-0.6.0.tgz create mode 100644 tests/latch/latch-0.7.0.tgz diff --git a/tests/latch/latch-0.6.0.tgz b/tests/latch/latch-0.6.0.tgz deleted file mode 100644 index 836f7cea5b726168781eb033470139d65f302493..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56983 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 Q{h$8+1HP;nhX9rZ0McX*6aWAK diff --git a/tests/latch/latch-0.7.0.tgz b/tests/latch/latch-0.7.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5520149b02d30595a16d677402aba33ea27d6702 GIT binary patch literal 155658 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 Date: Mon, 31 Aug 2026 12:43:22 +0200 Subject: [PATCH 21/26] Fix spec --- tests/latch/latch-0.7.0.tgz | Bin 155658 -> 155833 bytes tests/latch/package-lock.json | 2 +- tests/latch/src/spec.test.ts | 2 +- tests/latch/src/util/spec.util.ts | 20 +++++++++++++------- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/latch/latch-0.7.0.tgz b/tests/latch/latch-0.7.0.tgz index 5520149b02d30595a16d677402aba33ea27d6702..8cf24f7761888e619d075d0b7f204bbbd5a5390f 100644 GIT binary patch delta 126201 zcmaf)Q*fqFwDn`#wrz95iIYhtwkNjp#OA~{CYWeq+qP}ndB6YpF3#=gi(OrPv8%ee zYFDk*zZKO3-`WdbORNesoPr(~?@Gl%tPXz#-nM5!s8XO~vfT`{wUNAK7wjkR;eP+`2r~J8PfFLQ%VYzZu1e0NPTZscBBh%(gtiCEHycS7k{D zR6u$HRoaI`uMun>Ym2#3Ha7G!S=A2??n+|5)*Ld5A!*}xoDl$ik^}aS+0RlHqfj_* zX+gNaelP_5VSZ)bn?&E&-Qg*QMRaXlz7oFpsZG7*IMH(5C2S%HluRf&L{)DedZa4C&mnkZ=JfAWj{_rYV@mu9`~>4thxBw)}<_jxgzjH!^9b1Yz;{qoYr zr}0P1K=KQa8U;AW;~pxXNu-o#99dBK=NHzfXvho3h&$nXI=Whxz@>?gh#(y~U%Cx7&RR|scxEGE)BXg^y*45~!!yzXV0O@{9g z{|qwvt(<^o9~$SW-@jJN)jS1G!ThTA(v}q*7MPB4vjD_e|3zc$34i39yhGW){9&IQ zu*2k0{**Bpcg=mw_d{ZgQh=Fch#T^?Y*-uAW?wL!wi#34SA;c)0xKC=ISn^k+tuEJ zjn-Q`X%X`D?b#Z8Z@JVOPkaCBA6-4I_2+--zB?KUn*vp{)Y$TUU9a8&V(B^8q>E-p z@v|vvbHMOeJ7+L15@iZ0LJ|KT{hy(uB=Vke-!X)sfv;V`xhAb{S4{U0iy2mk+h+BG z;x!DmkWYcN2PUU_^rY0Tr5YG{O^N*BG?C#?*`kzpjM4o^nKo@G2FFvIhFKJm^(C7a z3!j+hXvuTNV>bRT)UYr3zX*R zXN>cE1@OgWJ3-@>IIq`lL-p;T>F2L;8CaCYk5SgQyxHb)kr!?RS9rP+B7$J*5d*W; zx(+O*5*-|jJ-wwsa%J;Vr`|P3usQf2H)C^$Up z0iBh+HIM#j#Z0Xo+w0c-ipL!l+VYHKr$rBbMVJcF@&W043^750U0U)KkmCq0a9$vU z?ar0qN;;JGX)Vz^1p;^v86#|r&GI|pA-uv34GY@S5eg?OG^8kI=GliGK+5XOMEpf@ zE%mI3FF-b2TSA3hOkPhx1|QWimku5WXhkN)dI)Bd0jJQ5c=!;|E*Lly671y2bw<;Y zW;dLm!F(tJzx{e-x5CJo?SI>WQM5z|p#6vcBo+We*#eqKzkpU|p)5_*i9wZR9Y{^T z)B|spZtV^r>5>|RXw56}*yRmDu5JR^;l^QGVe8OmxkaRDQ;f2?poP{ybYlVEF_7DOyXZ)X^25z5M5p?X!Kfov#%yx?ZVzYXENmAg z`DlJX4OjJ>psKj!8CZ`6VQd$IP-qnvXeV0CKm{Y7BPhccyL^GmN$&&^U~y{n!i_L+ z{PVb2=2NXuWVD=UyfM;%6D50Gsg`6gm1w{NpdA*klSL^m3I_Q zzn+Fk3nRUzQ2)sX`VeDkXA^dQ9oswY8Q#T$EPao$D<+G*HYWG$rG>EB561T*ryOBi@ZhF&j!~h50?nfk|u3#!F3U7g)~vA}o-Fw}x&6q||#^Y;`LBxyzWZ zhxyLc%VWwsH3(t1#)KnzZrPiUOkC4J5{YK!_|#_D{bOn50?sS`kBV@R94klaq5o<4 zH6L<9T(5*JXDRIVSO^Z|yH_vd{p=`h1aTooh%#iZ%bj+?Z@LdL;(yR`RBCL$*VqNa z#P)*m#{#z@JRlk0fIc0DAL3&0AS1WJ=cCzN)fC7&8CR8f7%ma39)H+{S5Bq|0sANS z_7mp*7_hOtklh`9Qi3*g46@o533rS%a;q&?HM*{mEbWh7L2E)T=cpDAL=t8zhNJjN zCxvs2io=>d_oOLRgNN21eeJVx$0H+;Od~7c>Tu3v=o?!J!%IG}UZtaL;6SxObZm$EH;rC1nRi zsV*tVfm0DSRgukVnp!DtC4XyJUi$hzn$ZNYW&@)hi6wk89N$Ri7eMK~gepVt(?e0l zp!T{ZJ@;!-l&jC-PLR;)bDqF2zU{XpX)W3$EiGh#Ye-_|9t;ed0X8LG1T$9Tb-2px zoz%Oy)EFV%9@N}XS8wtH=&b8`T#HUWeZtBLaOG6;?o&ae%^)LxE&hgY+4DM(_*H>C z_Z^Az8!&*c_X}S8qwK*9+z%J#V9uYqN6|aE$80xZCpMFn9STgS?x6Eo+dq3pi1R?q zG0+QeAn_%9=;3w`l)n4JccG^ z7g5sFF98IWTo-sSnvjmVToq00sN;-KHC_up1*6rB)2W)f1-QjzwS%f2rj^lCWEqj8 zmvMT~4?IY9957ZJJy$ExV2|2Q{5uK#rKSPm=wyD#8;+g}EFvR3`YWdQ>m>_h42>I)?r>u z%syLnj;q41-cJ(dE@>@<9;rtQXVdkhr`H=P5MtdE_l3v^>2S4$ z=zP-`tm5UDG*F=kWRQx=Q{O1y0!m?~8*1=aA#M865OnExQIxhT7O>Z^`&xth7m|O* zA*VE!Gvf-Q2&quT@1USC?JJ4VhhjxYOneIRGp(z^3oIFe;oDcRD4N!|pJicM-+*ow zI#VgIXJ>W>j)lJtC=ahLONN@5vlN@chhe&p{W)XbExgCMI`9n=VfN8#fnUW6ZO&%N zv028?@)mCX2qg-S3bPgpn)POohq1fw=uJB4PRC5lB)U|okEmXB)SPjq8}RXjBcJF? zSr;Gmsbo+EPcLM4Rc94X8gVVA3iL3lqG8n-eh~p7TuP_zCfO1?8p$*POGKoppMy;> zUPH3WIV1zuP!G1FiuyCEK)(-KyRIBdhp^t_`^|+08Kr*yF4|)Pfe9@6@==JUu9KJW4su~+oE?c$(T zyTTJz2WLByta`qnCe!|aw2r`zEahKbZAf6LaCpX!e@{O5Neu#(fKM4pB9d7#^b*6g z=+VHJh-k~XVCahOdYK{F-Uy<qwbC#q*r8JV z!V4pR-(;m_;%vnkQ!WW|B7<$ipb6#47vMq9!*pS0QS5s7x~d@Ml8QPb&1`c`eaJBn ziZWA6!ScazkAs5(*`M1DQDStPFUYSm=ZB>qMG@=aKb7wRuY@sTze4EpxmOiljp=gC zn}_paR8Q(5a9O4zIvw+%iGP|9%VB90=HSBigcm?hph1}-9c*_09WrTYW?`T%K9Ny1 zB>J7(q3NVwWFh&RvV_H{1i4tgaVx(ugx6I!8V70cn&Ndl-rU#Pg$&ZK+0Is^hVG%D9-)o0y9kKrbk}XMwMfC!CE`Da^y! zUHjwRJaqR5KD*N-mZpjcn+FF8$3{gOyltD-Sz6_#5M%6L`Z=FE5*o*Vd_8M~X-c5j zUWTsCZXkkgF2A+Bwfm`;lYj7Ba8szC-1k`qlWn;-Ac~gmVNUuD>M7caj_VtEMyYZ< zw6#fKtYe^i+s@}hQX3Au-+<3J5SYq&$x*Yk=YGZK;G~Y{t!5vF_IO~kEJQAcrNmM7 zfnK%h&s;`IYDc%nr8escUJ?Azom~VG>0lb9^BM%c^LoC`)W#!(gkDHmdKS)5ZGuSbg?cUpWKyFnmz|L$t(~QIzUle z!pyT8V=_=>9PNNW4xdEXg5dYXJVk^CLtPgHv?0(_?y?T(&fPmXJ8dQyN9T5`A%67W zwk%3mxRrjd(ijpA4=X8U>lA||GT{SH&99>L`L|Zg%*#^l)fJ(`fuQQ7hucd_9gzK* zxzo_GLyT{zH89cvPC;{ny;IjydjFB&OoP4$TX#o6i`Ix>Mu&HbcOfXCVB7|UAzKFp zAY(lI6(fg~;Fes@2rJy%jUaz9WFMSTXoJH8Ly=&^KEZ#+a-VxrhN}gIP$M?&PK&O@ z&0Y2;=WVSND1}Al#}I|-N9C`UsIfl`=H=m>lqhhP`lN`aFjZ>a%uAR5ZGuyV6@pIO z44Mj`hSUw7gS85Kkt6H55MN1jf&p6xOvlK4K__(g!>%k!kl4jLfeY`>VCP>j*mqH_?V$j(=djkUZmeE!Dn>7P+9+6|fW!K(wL$p7^eYD67JA`EZJ(s?jR_-x`W4t=MbsPd-XtA}mpQ?~KOd zYk}#k|6Y!RkLDd@5?LN04N+u3WU5XHL{!N49Cl;k@1|w?CaC!FwlF$0as8{59*Rc| z`DGU$k5IUcAOiy?C^;J1^n2f1^+PkRe5o9V#n$M_aW!`kOz6+72joSc<}RO66u~Qe z>I3h(r%6g`mP6C|D|%?)fQ7lQL?7)-c+GzZdvKcAgF80utenEYp9DVg zBM`HLTL^UvVuWXXMu%iUZ)9~J@Z!h~p6J>OSTGu>_!S(8J#c$riydQ`INn4odKKfS z>w?=6x0aRnmM?Y1c1#WNMu8D_J@jO#cg^s%oCbJ#e>RNP6pRX%2ejc^F4{rTdI1_X^x8ZtB2_a1L;@+}a+EM(9^e>76ciTr#BT*)afGZ1##>Tk)R$ z-T>>wv=nHz*oQ~5Kh}+IM&221%Y2%H3##c1ztKxiG?pLsZ_aR}HG6zQSK$<3M$=PE zKJMfPwa5(HAZ(`hD6%yHdMWDCDhOq*f|n?vz49JMX@kU0Tr(b+4l>huOfE|5zm9eq z|CXc083zk1(g&8#hDP&(MOwL(chN6oA9tc|LK24(@U5BJfs)%{?!}HUS%rLM0d7h!c6ZBIUR6jhwxyks6P3F)k| zFzWE`H|DDUmTM}IqdYPp_l*Hv6!X;&G_LD`t;;={xa;w(64m>S`O)kde3GM){iUa6bO2l(fXqBBHcUirTTmM;*CrAvs#Gi)9x zF1Qes+?nd`k^-tkl&;LCM5;FPYkQqVs!y!kmq2g+y<3t!2%u@}y@N z5&j0=q!Ugoc&=h8V{8!ErQ4JL631OJ-N(uGJY4xLDUnM(WihA}3xrhBzoR46s`1%K z+z^=>Kqim%-#xeJ6v2SdaK7eal%K{d@dzz4>L3QtMvvi`-hR}uc755}b_yX>$ubg3 zHToGBeS}G5~}>&+GvHX`@MWoXW{`co>uBLTe<&A>6X5ZW2g~R7>)XCpb}Zp zTc+H>o0QM=;kFN|)q-8t082dtIo)?VH1kM&J?qwhC@*GH0qIk3S?o zwc~YUMqDFv$?5b#t~kpsMa$9r){4C=;)dwaG)VYkW>9usy>7Rf z`UGo>#y^Z=t@->_;bGWp2Q)F2Q8oKrzLM>oi)r}gGhYAy(2ad;n$X4JMx!0h6@;>iQ zd(85DwEAd@Li=_$X1VPXX&3$|;c$=u}!Kp?#4WXsmEf-oXV3Fdny=IM9VJOu*rrf}_Iuu#&&V8Q< zs*sVw@~wQmjFT9U`oKv2rL1<#ylL_c52n5(;t!i+yAg)N#%9+S5~Wr|JHZwwO8gm7 zjJv6ZDw56u08?0ikKAX_CK16UX&!fQHd}rO!QdW77TIu`8*Bv;bgJCY)`_zZzxpCZ zuQ8GyKNIRKfqH)wJcAqQhP;p0%A=2ym{^_Q`NFa%9Kn0!U1$GivHh(D1qix`9X&Nd zQp0$I7ikq{JE;tQ2%RqSS2x=2S=&fsSj!fScL{_8+LpIFzBG@%y!rD2iHxL94dt$Y z$xUWnkf{sf2;fljkyaE>^CrbB@wMcrb#M)}xfdU9#z4zjLnY^ zpt1;Da|I-rMGkUV0U3jU)z6v`%MGJz!~?)42+wYO5`=Tty}kE&tyoYv967sP`Rg^q zt>wHDWa$^R{WkfO+0}^KicGj?i7kw3r@`Jr$~tZwp=9(DYdRS+&9bZwo%&)kYvL5c2mM@&+Q7j?D_T zHq_kjC_!DN!FdMZTyJlo>N!d*og!};jz*42eJNTtWCkt_(*c%zHD>%Tl48IpuPVxm zaC3-2$;VOCjf@DK$t!u1);0RfdVOkb8=sHPxEPIxPR~()X)O?0p|aj%n2;njuj2!( zz|-5PzE_EDm)@*Ov>buVm*r!8L*UMY(Z(Ik7YXdDGN!kuKpKiYCi*?hXhdndtmIx) zD!$EfCoZOQ0OpOmkl7)A3?7)ita1Ij^)*0rjs0jj6+iy@dN09z(C9g`p=lO7Vz>qj#wRfaG#w7Hh1Xrt|H$+G(*BqeJ^KQO6Nv_r+nimP zSc4kZ$1mV=lj*71{&yW4S}; zX5Sp<#KsE}1Yg;)9bv%9*3&EQ}Ab`kyysIg1wCG|X(HaY+e+NhB3Dol|!duL`W zKm^E@#RoP-oo5n<+9E-OjMmSKoa6<<(x7xawOY8c@^ZOt_gE62NOkU3#VCJq|1l

Xq>Dr1&BBD_EhXJUyI#XZ6U@4=INEx$ym7RhQJ?e{IDI2G3Tduk=5c67ZK z`k2aoJlvsf4~xw}l$kExiVz8=peZ`jXG_pIflhgL2`6BXN)f6>1qto$T>_b~`PiHJ zOXQ7>03cYUMWL*!;y(|PGK^G3i<8RCAgB!d`Rk{?MxNB~TScdBHkv-f1aC?zWUQgV zIL#2;?QAGHA8h$6+sjvu>^+4H=%r(dvKarT!uxM}=&uwv4}7zb^m=%1(j+y=x7!BA zn#H=-)uMnvwKZ}N;^R_C_pcS;; zm6|4dKcCP|w@NAtj#6W}3^>CY;VI{jwlO}p$c4rsdT2SHEiM%+#z<RI`#Rdu)LuAlAMKg;!|h6G!B0i z_ZGO_YZeTFQ$iaBU-GwmQ}t-Z34F894xzGu7(zhM_AlV$`h1RDUmxg#2G2*DLC@KK5L~azp_! z@=Q#A?0oDW;}XRM<;>q!6v%>^q1HkhQt&MtabpxjHyI}mJe#U1}njG#75TAC*eDyJ_wrG?%`e@>W=V>r({J_4_D4&u3?CqS)VO~vqvWPsZ zUw6CI@cOKNYS!Jpy6Ee!_~KY>GUtXkroh#9Z~7U;)RAFBF+R0~G4d_6k`j@z3#$GG z;W<^`uEL{na0_R#krzA6$H4~ig0wlaJ*#~ZaAFt54?Hza5EHB zCUIVC8k3rWjkHdFau)P80dQ5f0g?Hb{WwCL{_j&O)mn+e40);uF$dw)aj3llr&RgZ>;D!DD>FcGFy?c8*R)G*$I5kAk ztXL152=JSGVx8X|y1}>$=pE=nAA44}fW512=%_J<*MOWgxb(Q(2_kecPVc{b*%@0# z5q}1zH!ETv^t@|)Sy6MT4oSx%#tR##i`bX**wWy3RrSzSZakk|S!UZji7s4EMUhg1 zcADjjK5u$v_j(&!b7>jBET)kn=n#j#u@RBqiY0_Z%|z-6_t zL<48oca=WQm2F(L@4NEc$tD9+lJw3}7WF?nT|W`5LYjWi`OnxN2(9sW6Uqpa)ig|m z8!}Q-PxGw9(0es6t+vXfje6J&jU89L4s<15X+|Yd>5IATD0B}x9F^vMG{jIT#5%zTZvpaXo+6_6?b}OyVI?w*R z(L>8f*~?8$yLt{a_M;0=eQy1PG_LStXRIA&mzxf}&0^^d4WF=ER-~_wq_{D|KO@3E zW7G#m;P2P{S@)C-cC43-GMD|sIJhlJE_C^M2~7bWFmObw6tA}zR(l6-poHt}ekL4B z^v5pDo9qZnPyQY5OU{keb?H##xU+~0dmC6`j*ogVTP^3n0v)-6YALvIdFGi88*)U5 zYAlRuziO@cGR=<|Z`jef0xywopdhJeD`Tq4rYDGszLXGJbWu|%FW~~}PIZBZ(ixfs zFppnqIr&A?>5zx6-M1I)ulOM4NMNc<7+?Re-Ma$vq$FN&ujMxkcZDD z0DZ;>-?PnjP=D`rzpT?{J62(WV|u+*AqB|<2PrEaaR?XK3)VaaC_JUBd`l~HJTZzO zCdu`r^!}a=?`Yo7ZZ$kaWeW|igFuT(kIX*j5JE6))u)TYJ?j!(CzO*EbKx#Yi2B4< zidLTa++%+KTS+x!No3WNA3WC09zjTS=cGbcJi6>cYntnx;#DzPxkEC!Cp3KktXu-Y zzObkq)*34iGSu-KP^2ngG~0Y)dI6;qHXE-M&>w=U+!OLIEI(*I8&=Kzp7Xq4(QL$A zks3Q&*Nb`N-;RY%zio{$Ja;GLm{$&S*Y4 z{y!&~vmVc0lw^VCqFtPIY)*@xYfLHV6mJI*f}AA2u8P?$zJCzSM;cqn<&@$FcBDMQ zxgv`El=+N*Ql?f@#VDs>KCMKl%%inA;mAj&+!!+L{yJZU`fMBue@5!H!*(XW=%8kM>7k%FvyE=wWdot5{C~ryq?(~JdE0Pf8EOri?WT$bpuf7eTdYo!V+|A8^8K<1x)41+uqbr7N)VOf z@Hh}W1B4J|x)LGIcdbGzy~Pm}U*yIbXNhVNN18oZ6d1>7r|B~;9y+L1e|MY!k>;_$4T)gaoE<)Z7TR_;Gpb-*AQ5_V&pCxHA)%U|4)b%e+ z6zP1hj(1RjcSxjK{|f$`=7}m;=BzZHX>`fh0HP%g_v|hrXZtxYE;j>mrDKQe7o7U%FADe4#sRZR|oCz`dvGfbE zywPYJ)iq$Dw-A$Zudyq{cdw&X~0*jDPklHZ7yeetSgsNV5)c z0`$%@s8t7po@BI+LaT_dD+{_VM+E1G)vyFM=#SoWssBadwn6SbWsB^2F%0%PmouAHLo z_^`G=D9imSd1{eBFY+0&sk~*Od>vdLAXL2Z;k3rK;~FIKX-f0|%tRplt|TVE+$Yyh z9VR@dlE~*7TCb7Mw8bC<`o-;uLgVLrfoitHt>^#gUK-p(jXAZHT|4b1zg>0xK420a zD{G95X0kP=CXf4fUtWKBwP3gk6$%l2Rxz*(9aXh?Lp&o_Lk3CEPk5R0*zhnBSoB1s z$$2JGDkI+PcTqL`@^_f8socCOdCD-0?<+wZkHj`R(tOX=L6|2-;7b|lFr-mHOYe$- z3dAJ2M4L__iy_AEYbnGkIGHJIW>t29xhA1EtfQnZD?^}I+3@DaA~)P~k~b%d`|?0J zgKfd>b^o>=!0HHcux}E6230aEO$X9mafRdjeQctyi z%$j35>JrLh|N0Swwjoj{o{JY|xJTe>>Oc-g*M@fgHM+V8`y_n=RzTfKUwL0A{V+@* z{2?fT^!`sfZ!ctLmERygoAo--8vHxx=Qv1lK^$h;x2O=)pd+tvFdASU4QC_VT!ry9 zIL=F*bfa12*DJ*RxkJN`-my3)g7HLRH4k2+ z6)%M;4Tp^O+|OO;v3WFp^9RnS?G6fal3?b}0kZ^xP;RJXGy|Z^sA3647N{^E*YZ(8?>`D~5hzfD%6;zs4-l9|9J%S7o ziQ^O@|MgwSirR&iYS;oSZ=P!wZ6iZ)f(P{XIBR5K?_C;qVB$Tp%#R%ttUf)7UW=;NQW4;_T#mi@ajJ&7WEkT zUvm}!Zr?#RU;EJ`AoA2mN{~bNi$n9x$MY2}a$1n%70xZ_KL=_28e|8Di2pxNyxVQa z_*F>;SqTTG+X8A9hu9ecLU&M%LE$eo79W}z2JXbZig71oIRV-; zG{mj`o1{=86I>_8-AdwexF#mPRc4Oz>?viId2*^E z)C2dz%8u}lJ%uiXs9YKx#RR3EdJzzm^{8F;ujz%EOBhOF_H+IZ!Ok5k&vVd>oxDHmZoGBOw4u^f1B;ZG*zk82|_AnLWnNSs(Ln|^MRWC6liD_QvQ<@d( z+%s3dmU3G)>EL5KzyJDS=v8+|k{d|(Sk(`qN45r5181SHFg2k&-^X`g&ZI#9qHr8ngGaC(llTTSSi5yKzLLrn;s!_cO92T)#+56b~6UDcJ7k~1;Lxx zKhq8CJxpC6o*(BNS$NY47$!eZvnPK!$Cp*R*N3{wZG&{SRF+WgJkqh!Un7SwDCx|F zZOy|lI`j=-Q|&|qJ1xah%Y&`%YnQTI4EH_L7j0zl?zicxp3=)Dbbe7Gg*yX>ad+!w zeZqrxCb-ofd%hl-hgq+O_}KTj#|Alm5ut1?|2i}mahUFK_MPhPc>kO_q?j-uG=yd= z(zHD{7XEY}SxUC+nLhax4LABj`lEmj)}iVz%PT`%fqevOAu9crLt zF7&-%Q+-qaHmdIlK{NH3D%S|;H68rR3q=@tOO%$oxwLqQu#BA~x-*==9w(u~hsH3M z?B_HIg(03cx#Ao8L%=9a^Vdu;)w= z4jV~MFp*A6^dVh*hfW2`@0Qt3>-5QV-keuZ60Vt6%5}dNjFW#YL60bxqG5I^K{TVl zG4`YgH-`p!_533b5R&@_@MHJwFk_{JL9B|mWUhJ*ET{@$HQJi!j_ z$A`xvSs`a>PvaME_e535t2bh+qnT1XaG>h=Xj#Qnm@)DV+if`yIjeFYnN<8MsK-Zs z0_|bG^+K36G2HLbd2nL?0Q#yi!}!zdv10OJbiH{Z+Wpr-eE{qTFrL=05B#0CH~wlB z@tZ~{iSS#*AD^%+6D=jwnlR|T!Wf$qq`TqREys|NCRY(RveS> zlPCO8y2gc?lIqN~byr_gv`S%~rx(bk2VUH}n^S6(5>Z8$7CG?t@e^=IwjqOHGK*qR zQ3v9r`AF)ZB`w>M~goobwYHu zFdNg3ozjH`ij5nMUxZlQ8^317SUcAi-XN@+dJaGIgqkT9&>#F={~lKRk42>g2BY?XCFyqiA4cs2|M7IuOuNSBzj5!W z>elH5WhK3EEGnO3=FfD9``*{LFSL{YX;{D5^A3G)8fiQgqw~KgbKKY|!0hyY53;1) zDMN{pGsX_5bHe2l$#oaRm-l9<+pm2Y3Y~z2YmbhSmx~u;qG$Y9rEJ*k|Jqi} zlr}2?oCbFb0UD<8(?a;#C3m;Q^Mdm9cjSumu}jB-cJZg+Ret3>P)Jw6Y!79gS z=RxBG>~heD5lrlVWxBlQxyl)c8Oo&O=ZZKoi|tfc&NRLsIWDi*oWphsL47zj_p;z` zfqBtvV%*FGum+?zLqw~&XMroSZxkCQJt6l121PeG9Dk;VBv=dD+secLVANQ+?VHv| z{#2dAm*ay3x}tSnp0D(CBr`UDs>+i_lk9>|VxxD+mzU#u1(CQ=87j=U1Y=?AqoeO{HzHW`(7kQ**0|y6phUhtycvraFu2&XRE=JZRsWSE9HN4nsSis z-9fMM1)rUAQ1ZE9BxV4#jD^MapE;aF(io#JzLzEd1Fwvq=Xs!yWl<30*!rS7x65zT>idE2XY%%ILIvLEz$1iUO3$zq3t%5%-8F zl+z%VQ$!1zHtla+XXpQsCJ;HnEm4DC*PnD_t=k{H@l~R!xhdP9Fx~M*2uG@q>GkRb zi&KOEwSe3Qt%P}Xr4v*7p(2CDKcxFdtO@)M?ewePDOoIm(b_AB@%2Aq)X-LC)E1`U zcxfToh|{hvT?@fY#B%T-euoAFj6{0fTfL!8#f@JlSUEpz5M)kcbrWWY#3XIbM;4RS(}P=w-BiRt+5zLS6|*yKm50G58`LT&oehS zcZ9r3bpSjWqdO`I<$y_a|!b@tMeH3c@@h5*ViK`^qnbw&ls59i@O+F5c8 zg9Z-bW$m1cVFbRC;l3*{Rn#`BH+bpFg%K6PQBLRe=i=LSraZQcW`~clp~C*9W)wFliCot16F4Xrr47EENO*RC$>Wz@)(nsn=&3gda)9&JXRC{LMkFZCne{EymMZk1 z2&fWebjE9i>1gpbTN9N~dTYEBn(gT^d}9y_O+2-qyBUcp*!n4uZpANkE1Vis%WCC* zpWb2CK}{mTAv>icAQ4EA=N1iBTw6;Q9d?S#)I#x|W&UUZEL{TY&J{=gyf5_Ul=hLr;#YY6BtSNs;=Sn0vTZy;L1 zj~ieweg20b9TggOt>3z}7aU4y%ceA^$ar|$cmC-A>$>Y@#@W??Kl44Q;Cv1`xqU$l_3&2T5Q$2TEJ4m*JI94tTDw3B$Al$zzJ42*!z2W zn)yM+eV)S6jKug})*R==+#}Fb7UH6XcWIkA4ezKRWHthrUF#+}+3=76e~}(O-s-f_ zR|aIT^=LYMS!h3{@}4oNkECra!oWc$Y{N>>TmIBae(v)>sh(OALv2je;TG1pu=0YE z@q}kgeu6UoA|dnEDTTn?Rrpj7B6CS(7j*8XnIQOR!q?&87&;mgxf{H25!*4y&1)p9 zl}J>_=WD>nG8@q{x^j^sbIa)(AEKD5{L|znxz#hIX<#7ohe%uah;7A7?z=~7PP2$J z&h0RM$M>C8j$J)hNl7mPWqQG2J{%Nb4H>XX5dWN9gO-`ta5$#0)*mVUS8T-r+c$a_ zfyCYHE13%QUE@7rT#kLVU0m{tY{JA)TSTc;>Q=y&W;e}kb0~+bL#T9&WMDDVoh9i< z3)kSA+-zMuf7%4(2p$&EH%HlwXb-bg8SAQ_3e3MXl*C=8L;ksa)p6QQiv&!nLAX6< zL#wWf8Du!)6YRHAT?g(I09nr!cORt7-H=Svplhyma*)Fg2ogm40_p+5eu>_I0zs%B z0PsI&puq!>*;I4pLmu@9$lk_sd=#|0v5D{jsy+mvdeWV~C;ApueS2({-XeNkQc9<& zL4~GIh>4MLo%HmCn1_g>c2T)`5~(bPM+?Oe5V-V+%;v+=A7q(|TroB%%yJRnM0VS$1{~g&NR!%Iq*<#)w(}B zAIJJYM62r~}ttUWx-EQ-sxs9iVp3FbmNXrkH{XIP80k21X{Ev)S z2MrZw%RU!pt+DGLnkNqE!<3A2uZTs*}Z!a1E_Gk#Y{6ukv+8{DXKuSHB1 zr?W7;6l|?B^@46er(?>}>+Pzo#q@2e1F-#r71OE5&AF=}5a*kI*WPQDFce*kNPnq{tJf$d%CE8JX(IasA_7I-2qw#jel;gYf+SW}b+hqXMLWzL(yb$}PG!yaF~XXFr?<;)@`WuJty@rnJ6Y2f z@-VP!->j?jk#?lScmY~J2hpp@%%61ZPdIBc$29UEB@IvkgvD zO$kFQB4F7g29x3yCPl>y_~rTtS_VD=rlkWU956NKx&KGnTSVm*wA;G41t++>yE_C8 zL4&(H!QB?_?g=iz-2w!6cXxMp{l1fbpMCGR`;OB)o!O`{R;yN{>YdM=^$9{fOq&tg zOjx;pI+o)3fQ>lrtgh&fyS1Y)rYV_7EfpxPvwOcY8 zLqLGk{?YgFZQCpn?cCg)9Eu^5HCN)Vua3x{&Jd+bUV|l|rTN09x0JP!fANcSIMiAt z&sWpA%htpx+Z_b9k!&dNzy)}(p5zR%_p4+XoW}kt$`J`3`z{bwlC5;_}h3BVfN`?*bDTs+%vkRTS zcavfo)u|vblL=!WTcjs{R;2b8m#ZhhjKzTCeH`yHy6Jal_C#(X5b}L0LMw zK@zd^!Eq_UX17Y2Fk>E%TjOJ7OS19FF}rUr(=`8HcK$>7EDo9d(J1hX_L(i3=DAx>1Y*B1k<`xS#U>|!JO>LfB3h^41*~6x?k*MCa zwuYAeZlEu>@q2hdILqYwzaR!b^U50RVawYqr{69j%L`VO0b{S#AB;imDq{idPTv_o zy2Upy^x=HQFPIia$0Q3)ERMC_TOPO<^^!2i%I$H^6Fn9eGIC_N*Qg!K6_Yu?x3O|t zdfpsaguvY%kEY-|V`y3;MjKSM+D6uJQE>Rq@=)O4Z$Ca=u#AB45UstjPKc`A3FJoc z8#*g)1WAbF1kVM?@M;_d97$>|zkA)72oV^PF)6~I3}EqLmnj7xc3X%LK>})In)Lm} zzLMHXdz8qyHS~PEw}fHXy?fI8vWR;yTLdp{RV6LtgyCppxn$f16s^fE?Qg z=RD^MuJwTKo}9qRT@IHy9zdwB-i&oi0T!B>kS717{mxA)YZy}hhsF$mpW(X)fsE*w zsic{YKvx8=s#;951$S*A-{d;5egH{|Y>b~W)V~VNQU!|Y<;cGYZ>0x|NJriHd9gpH zEp7f}Kb%jW3e*41zzRe&c;jLR%|00WbF%gAaI~{$@Yb(#Q(mHG0g_s%vmtzsY45Ie zb{GnOf25gG|GCUpe;MYUVJ9yU{;I4kX6Sv1e~;Cpf;5W7rx%ubg!Y;=2Wev~UAi9l z?az|IbdY$E`mTEiK}KFQ=XbC~5CNkr&RLRV9KAFmCuG@Ixjd9#=|u_;%w^a&f_oJzA)Xv z8S)6E+q>QkcMo%*l(7~HqqE5}QlmYJTg+h&oQ@Mh<;A)wHh_Z7GfO3T-2^XBJYYy@ zFnGl4h$=g9g3x02*!Az8b$l5Apuk5N9#Pzn;JqdVi?qQr7r3t9PY+4Y)YI_OF#U~O zLmBGTpVogC@TJWs1;pNChPsAWgvd+ld+XMU63o1@@Bo|=?oNx;Pm6@&5xymZv(Ypv zg8I4Lr6nKQV1P7&{#(s1Zl0oMh&O6>qU@Jkx^80sLeiMmZBbU48W_Kw<==(r4W6Ln z^*bCyrF)!nnW83*FkO|+hL}!%e6s`yv2P_@O>d$Pu)<;&8H&!Ky`3om^}3OZExjm^ z_aFp%gltsjB0K!o!V%wMl*Bj)Z?EtJpCCiza{bd5P9Pcei!#AEAobNYG1O0#hyGAN z5T3(xGw`pvWx)O&W>N^`s`+(%P7(T69bOB3nY z4VhQ^OXqM=5O$Gsu1j?Zbt5a;HF_4nFLB+UUWIyN^6+)EKY$c@u({l8dRVql_@w zZ^#)7emd+#yYOtktPkn0)*o&d=haLN1Gm%r_<$g4Hpw-}$-A{XM$ctKnAq{DTu?DT7!Eql+aM0wxBhN>j4(F5SZ? z=v+d6%Dl;A_+e{5&f-PZa!whTnDU*IdI8^<+E*RV8_^_#jRwR`J><7)wfWzjHXJC7 z6^-?bq`OPlC&>{H)dsQcAReRLnGA_%y)3!h6$En^m@R~MrRnl;uT+iKp$urrm~(Ae~feTx2RkHUW0@ z+v2Z8jpAO!lT# zGa6y6SjpogZjG@UGsVKvh=ybh+ikQwK@k?Y7x20I4GvS}q9j^=>x7)tRk3s(k;6O_ zCUv?B$%$qo_&FFAP?jPv%WFHBS_;6DBh&}V;z+&O>|pX-Iclo?tn)l%4+ZBx??|Ak zMy_XzL2uW>?}mwm)&+DL{E2OJ zGuqW2E~bvikJojdJ__tbEx!~d=3{#OR%?339E?c_6*K8-b4&LA>QGAnr{@TABmvF; z%}nJ=UegYLCHy8l!8lvuGFs`b zrEpAKYb@1SE(5Nr9=D166)>{!dNNv=_wd*Jk(4(NnrQ)i(e`5Ku_g}2y$Al!@`vCf zH!1eop!-Lnt45fKv$2z zA($M-Io0{RiE&N}r4K>2oq6@(iv5uL?O-M%qIx+#iwq015Sp1`4!i|{qU8^E;9esK z{cmP|=R?U68vO?m0>Wks82ERUw-a;ID+nS1St*bC7W8k)`y%r;K$7eGQ@N^wY8WtOp zJ|x~N{BCjhnU63=Ng#NA$|yS!eWc?$cwm637=tTiw-Mt;Lp*Qz6c;rMloq28V#$I6 ztMj3OY(-heavaj~!otb;vU@6=7Kc>&Egi!peG+p#_v8@1QcRsiG6e%%s^5VD7_Q>u zAW||$qv#mbeN5U5Y+U{k%M{FDJ4VXHSWcXq1&v?%8DC;cWtR&BCDZ7m=qASei(Ah} ztmk~$w<*FV=L{Nf;fqN1pSLgM>6kTqo-z z&oI%c&70}{@Uo1@{T7f*FP)gnIgQ#TO5w774d(5JPRRmiW@i2awlucEfZ;z+dbCf4 zB<|KRCA{$cj}It$VPud9;im~SUE;w5U%>7iU}@_y77K18yam=f?{e1&y7fW*F@171 zYTb*IL#Td7)X7g*D=PO?!WRK8k51N6pCyN3w7ql>>L2x!4x2RfB{6_xA48kYvztrI-hcIocz4N|bAyo@n* z6W(1*Or(|-8)lTA^Y;F_8qaGydiw5bd0j1kI-36e^{Z+9=9bG3t`pxyym0-A|1%&hiwkT0+0=6u>)|cvVn=py)m(Iyo~Z!Pqb*sX$sK(rKPff-?3dcww3d3*tZ#0!Rc&Fh_H-)kJcwE8 zqnPr<3j}z)^CbPDdUrvENr++y&L;>d$!Wj(9Xv_Bj~nO<3zbBXE#F{tk$#0x-a#7Y z1FF_xyeNnFeX8WV_E<2m*Z|Ap_HT8!-DTR4#5^C_un`n>9N_wvk&qEC&5pI}!IzXQ zY3tX*_zuFtv^1~Ow6}cMN`xoW&LePD40!dw2Kp?=!q5&)vw{opEDF9Pw>&ZNzxp5| zIkQs6vF`Tm@FZEHuaoRT2M$R`eROr19*|q)VdN5?^K&Y|``qnZCt2=_=dg)Sm0Btv zK>R8n8+|b25y_L+Ddy~jVL}=)`V)T{`2lEkB1xF2gnESZ?9qrO`_R3|StFy^RP?lH z0Y*k79ZotX>CVmPl?NeRX&tl32R0SR4t6#YJoJk*mPY=6U1UkGRk{eRc+y{skTWL2 zu!v3i(1hg=MpmoIahA?={_c(ZhANM&yc;oEKcUVIaDkQ&b(n5{S#(thoN9fHnRI)8 zqA;SH1B;&JwLILy-E}nzzW_TP(_k`ilzHaJH(dzML zY#HLo+^Ly_z~lt>U=uSG;ZfEhYF5m!M<2PK`&5fsVrSS6s0`h= zuR;1&Kt5zIJhqdbKlK_Sgp;AslaeKU?Rpa**LP(_iah!d+VS7vXLyzAgxANj;f7@L z?d4Wzp_0KKfbO)78en$(Q-F=VW}6Qi0&n&(7=gspwKA++wHsoxFAo}9Qy_cX zl8G&>M!5M~AmeV%OKlMJeJvOPNc_xU61D$TVUQ2%$_M?uf5`y2cX}uP{e8bF1Ag~3 zx;UYJ60?fKAFK7tb9MDtSmQ;hN<*o^Z6ZmperKdV>hpWq`R(;){89g+TR|~f8vk8Kk5cddhm`$ zyOcz$`F!wm=mHfG5G0pzHOp<@! zR7?yF+hNZX+Sc{sJcTD9RJVNdkYumDOJ*&ZD?rN`jv+2p%5gFqOCe>|&csbvsVh~h zT^a>cvcALoos!ABW4^t1?#tiJht>MT&Lp1P9js}TM27DhwN$jJ+Um}S=LRUpLhqgMyJxOI}Al$r6q%B>jkbEd$)8eBb@c+7`1~=Pj ziJwKA`YZQq*b?H+@0GuDt_pS=8sptRv9M%UdI^~6IxN5XnbX?NYrftFy>F&qV+X1^xODv zuVv06uDVLI@Y&fHJ3*dO3CfCOZ4BtRl{msGsueSc+L{hRU}1bYLh%^W;*WWTy#FVfA?}w_u9UK*x*`dttM#GRrKpbJ(m`$I9&H!i;jWo< zzd>L06BElJAaIa``zUeB*gs``{CI;;Ohub*M}Kra_r zb~kQ&b>94W?(tYRIC*=%)~{*s_g3rD-CC=?`K28DCh6F|H=#vdaJ&}0ZzqtWRNvm? z7w1{xV>c&EI-w zoDre`VWOkMy*{=1o*}t87PeahBfJk+jLly#&V4}=1YhPT>LDzUOKyn#vU7wxGWv%a zzh7+<`+wjc{qcOdJid0gl9t(;@A1!f4;v>Pm|FFSK}8uleZU|a5kP&8!{$*Bh3M$^ z^fZxtam~_Qs)U%tNvVdmGwDN|kBI;*`qrZXPjn`|abb-D@%r+PQkqCyyTl#eFe8(v z#juwCMVmqXi+Y{W?<42f>2j{4CDTR2iJM$G11>;La=t8Ej$5VM~{{|FS{AhnIA@RU7pemwG?K}0+RQQ*w3~eympc270KZM{57|I z{nEVs9Z}x-K##^A$k9_ILAA+POb_l~XmBmlwdb{+9w*RwBO>W0tyMBN#^8G(LLd(~ zOgrQLZiMiT%G=Xu5AFtho6Bzy+$TIvE^S?b;5kxKS8#N6*(JVclCOISjv_9 z(|rTQK`K+n6b@}TAAdL0H?{wU-0o(}^v)V;h4 zwF}|AeHt{EH!q^rK;Ic?laKg`PlbzFU$~iDmedBFEYP;*6Dj0-&he z?oZx@aZ)VfdC1WBVzd!6AVgy&JqbZQd?0qU=yQghF0tyZi(+fmi;EYtDY|108sxQ; z0+Ko3B;IZ=3c4L-0;Q*drUvU@aS$!Ed*Vv6rLy_F3giyss})%~Q>dNV0PT-=PN#Q(lmw!M$Vvo2yK zN={4Szi2~r+O^bDZg+uzo&Ox~+hC}N5tE-|<)*6!X!?9bRCp*mp;wro&s1WnqE|G8 z8p~++txzIn77?7|g7QBgs$Y~ZrpFl>Hh}tEncY(|NsU+3Q>9_)Dszq;ts=!B<2_W1C#vd`s}0H#YJyLWHQ(`Z>2i=uZq) z&0)98CBW+l$D{=I`1O|@?2`u$1i`Z4$l&x$kz(>6}wZe?O(-SQOxF-Etf{b@jG7( zI3XjXgSu}q9kU6QT4se*WZ_MvixKNH+%a{2zvEHH>&hQa(p!nDrEI0oWDv@Qf02SJ zua2UP^#m|N*t>8)rWhsz4o<+AhxY$Yx%Z;eU@>qXU>FC=m4C~VU*XHhZ8w>LuFeKt zOQ1d+?0*Q}=2mGi9)lg&KhhD3;O~ul?B;2e?(xyFV!mIm?)NaRuX|KNNDCTuJziXi zO>HyQTzyq3j+YnS-)MY@y?!gQM*a79acv{L0e@DI-bByAwO~N%4b1{(-2H>NR1+=Op%1;h40DossCmrI^i}6gF6Tf3}&z_wV z^Ph~iK1RjH0ZA+?I!;OO{VwADH^014tQXIpe*|U-&40tzOf^1vi%|4JO#*iqt;Yu$ zD%LLY`wr*0qZd1(=3zw)1q}ON zd!7#te1v<+%>@socI>=wU0f)Tg13P^ujXqSi)wwS=;NYfMg zK6zFPGhLK|mhtv`qHhJ7&pCdkV#}Et&QcYhGK5G+=$AiOhSAb4M|(|Pl+dDiu^MvK@OuQz82EhqWlR~rxH$@O+1BFu=Qd0jQ4;DMDDKL^W7eXjR%tw7(D zG&2^q;u!^dOAQrp|em2)9FI+uYd#_4?n;Vy=uQ^*PNsGhiE{t-MZ=C z9OP1Bpc;22k#b%qX5+X?1NBH`+C}!;fX`6YmdDH8+LJOipY>$1Jk2Na6pezoBO4n*E!@@K{2WT%4KcXT&mqKbrlIOeC6B1jA zUaxt|MxkkzhNXnQfZ8>YfHhW3yQ*AmZM5fWCFWATbt#JWPy0+XQcO>_0(HqsgE&gs z{&Km}=n^)g%{@?;QO4-hux{E>x&x3hliGzG6gtDHeG+&y~;lOplA+&q5jy$Fvg zpyy30m56YeTlX*t?$Zea(~L8dOlrQh$n3Xk4uJ17$4L3OY8tM_yP21Ebh?_#w%)jr zOrF>$r52IE$!}OcZD+olCTY^<&&T9tiS!8}PMgc$Ibu$3%y8%QLmIF7%m%4Yt}XV~ z9!YH=x3vl&9;O=zx3L-cZPccfa-WIlPMZmscXcJkl`rI#fyl86^$&=X}a^X*IsihPFtsArO%e_*EM*_ExrtZ+EPdH~#=Vyk1MWD}8VoP^20ei^h zZz+aq54`{xSS(x347_9?>Sy>0$_)JTz!_*rjCzcf_-wnuS*H6J2NJv&kl&}K6rw`$ zg2JV}oAa5^1On-p<47)51r^(0?sW4jO7p-S&8XW*x8RLO`vfrj3-I5PtT2F-%sM~` z)GUI)MyQctCFvE2V3l2P6Kt>l5V)(A@ErWFK1P)C{sbJ%;SjZzGX=j6`IVJs6MO8U z!%NtVkUjfayAvVPZ%NYPdA#Ub+WV?mkr~<)`Gd*eUlmI&i((S@!_S}M8C}eh9>`9` z_bRsOX!!AEI1rZfO7ewpYkD_e)AI&>2wG zY(=^gS1JFxEClUD5b-Q!aoP@S4cG~tA)CQhhK#zGkB&Y}7^Y_?>KrjoBc-Ykns*yc z^hap~I+E$CYr^7Dr>$W=y=k;l&b}WeI}PFiy~1!z!fI31Td^rJ*d{Y>KJ-JRcR2n- zpWm2~Sm1*yKo@!_w-tC!}@aE=R zaJvr|3b1*8b3{RL&|0z@84Mp13B2*5Y545FkLDd1u3Ed80cjr~13hJ4+LdoTU4B-0 zWYrC>%yJC{cfx?5DueT1SRGivSBIDG{~)wwo6z^JKRMvN(!!$vBT&qF!`|$$0b|Xe z>-~(=Qr6UKFv@eDoG@G+nVwUL;>^D(UKm94lnU zt)YehiV6l&M6P=;3aVbyJmvMP`nMz@o=^Ra#UoQ_q#b3t3KRTH6UFmLR1OnR?PVpR zK*z_;PG))yA+ml_cuj7*A86NHUukr4ghfW54f%ZPH5>8--ZkNS8%C`iagg@T{kW_^ z!Tuf9EX-5CQZ{H@@WE{M${)QU)+^dgMb2mDr`yzC#t5r%G|HRyFL~8ep%B-BSUp9o}$E%Z94*G8fj=vZj z65T5q^nfI6cX^drPilo5j#@CUC;7vMiPNtQ>woo!56bWyxHMgsSD;g~T;T7=tL!9LRHR>$20Yj(e>?|^y!WIdB+l7nQn!BAk_=dNxr zEQs|Q2n7Gle$U;1@&CrO<$Nm&Ktop#BBosfnSbNn9sYDQU_W>1j)9%N*yY6)qZNGQ zKB=Hbw_h3kW)}&aSXXe#T(U~}{bN=Xe--m1n6LKUBYd!rK=hX^*6jW%`lCfxWjUWj zseSb6sHQr@9^x0bwE~uovhS%8+zT6@-Bv1QM_nJ4Cz#7ea=xCKis>$a0Ie}2VU8c@ zduDfO^XAqK{6YijtPObRtZ@ctn5VJBQ;F5v3{(6{?-Esz?by1Y?uU z9HsvG*>N?HCA1IeyS7uS54g7>uZCG)P+^mHWX1T=VhmFsj^qeqJtT2G{mv*z7xONj z>A^!Q0Yo8jPKr3MIxfd&<&G!HR6uRoJh7bDfkOa0j?Rz5bpQ2?36?DqA)7^}3Q4?C zl8E4>4q;U;c=1R}&K&E=#fC`Bmiit*S+E zahW|NDnqGjF}vw*unaW4h|9#%Zwc{dNZ_mi+EO-;%UbNBGz+F z34m*ji>ko)uXnhJ{Y>#Kc}1Y23G#R1=jvdB+GMFMJI5dAoPp2HNE-grx55Y%dvu}r zFOaMC&hDoRC7LgnuNV69R`)ON?EqQRD&xZLoA&Ci^YZyi{UH6B?7sEE42_r&)V4j*%o<&qqmtyuxco5i{U4N zSR3e9>R+>BfUxIy48cH$u8dwOp5sJ5gZ1x02i?!3&w=kWnF5r(sXFcL)!u)aoVtex z!`t;F9LJs^RA6T9gu0oifxtPktiz#l}hku8y;4eW70R+Y%>>1OH>su~-2Ax6bnPh%}5Nn|YZoFsLe zgG4Ql=)oc)zu_0ZASb3K#s<-~i|PF9k9djFUP94O4`+D04!CoM&=31!MC){|`sH~O zU(5i4Ouvv5ZQZ=cnD~~EaQGXP;2?mtFD8VLaTkK|AR@F8dMdHKB$mH*G6OTjiSs#7 z;X>r?6Tv*`|Y$fra#^*O}I67E%N_B&N)<{qp7Ka!uBYEQn}zS}RQw zBwc*cTKmH&DZI_JPO2L(+G&|7*gODT@C=>?Z3DP_IS%IVOz4yA&mV|U*J}M|(~&_D ze4En@15JMw;#6T#kv%7ODX3+d2w_2UMI0wFM4D^wwS}?J^6Nn-8;M&h@&8hX(|(ll zRrxs2wN^&ui8?0K_b0Na5M^m>>EWY9!tzuZ4UBpiHK9yoKSyE%)86$esu&0Jz%3@T z<6nSX$r)x!5N26Z9!fQ%HD9hAR)Q!U8cDr)xI0`(0CMBg(bpES3Vo+)g6$QAX^eYr zHmhk5J7u_RDk{Bl4CrXe8cam66v~4Vs~2OZB7W2!sh+)7&vR{ary_Kz%CxPX1I?A3 zBbvl%vNs75{aG~!m2=`8PjfrD*{;NR9}SQ#y-iYIh5xZ^F5-n32~m(FoR7pR1lP9m z;=4!)1aW>NOzzMpp(1?^7C)~morAN(*a^+FZ-!+1V+s z%HU1ZwU&k9G>5Dbaax$QDhWY0U&XBwLBv|05=QT5p*Q6~`VCJ83hy%7^903&FZ5D4 z)B(q4uBwS_O@pfBm`R;8{diuK32J{}b6mp8P|(s9CFqcF)mh=@eP^K$MJ7?WVV_2! z9~lK&)ey6d9!owrbFPaldbIlL_;BCb#cnY9niLKtIVsa}@K?$~h1uS@Gf|@8k+~By7iQou8e5+!svJUwT|XStMFeRW}VC zSwBHH^1Rnmm=rq>CqH|>@ZC?0_l#bnff44=v7n1yoR)rQ#F{wCUL`ts=ocex!VGl> z8iLQj`8tTZMv0$AX}(a9a5jLDranMMdbFwX8t7Y5MN9nV1qkL#EOO~Sa7Wg&huZN3n%}n6>8@QDm*G$s(Ti2S1=Qk_>>jziHw;qu#zW5SU1V$!4L7+OT?mG zivJ-ymyy3QY(Mh-uK$f|FJqSXq}o{jrMfV7B-e9!)0*znufq}CTg~3KKWl4O>H^dr z>hmmfa^#|`zX@^VKdHHk`3jEbT&W@f!@}~GkJ9&c$e4O61v>t^t&;}R%i3n8S`G3t z(0KbZ$LmWCyJ=OST5c>=mk{3>vAq1MsCtUFI$oPqiC&&rk>8p}-Iy_++H%{cz z(-YML^PC25{d3y!%!`y`F;T8?Sx~EFb8T&$1W3Cv12h8hU#Wn=qkZ!Dc>Q*ONT}|{ zFY_daO-PDktk*;aaa|BQidjgng_DINxBDHLD3@!~OMrQ0f8iQdHHO0_Bcu*GkEKlY zjJl#xho}`PN3AEd9XgLF9Z4&r{Wos7A#SYS3_^U3&StO4;l@|Wq~Buuk~JA{+GLoU zgXqzXrvb9cY4^p^yJLFv7wjy64;PVDt3i69iRlg}^ySVocjhKZimqll30|Vtx+vKi zr&l zPIh@gHkq%t+}S}>RQYZa_0wTzUjp#+km`SDYA#8g42I5r7a2k(u}2T~|0Ak<71Lez z!`GX^9__qwRB+=(d(*fXa79t6?uoU=08kzf>&u;IgbN_v?aS~_oc~20T6p`Ej_9&w zRJ#3EPP#sH?^ESUO?&28Mbm;!;AwIb1U09Aodq*~$77^tGBw>0AKo()nBe@p3xQ)7 zrTHtEP0Mgx&=NN1_GoDx189 zMt*GGZ6z-oTGiN}mcb*a;hX|QOj4Gez1lxDaMW-QvAif2k1JYq z(?Fk?YwwVH!U)j9myK&WyV~gqNJC`m4;%V)S}t|ZWda?jTM^(KMz>T!;e^+a%#UC@ z)k;G_umfi@^O6E2XR^-{6~KEOh)+bXc_`~eZ8(Je#t%n2w7rMtOB5WxS97?RvDZ(h zL%*t?N{q{Y=*(H_%X8VL81YH7!$OcsfF+_1C`Nm?kC-9Jb-R{921WFcvOiP80sX8A z;KXQAhh;EYlHZlR00{UykpRAVd<9QXJz9wb82P!T{hH!z`-G4Sh)Ewy?-{&y3U*|T z4;Hh2_Fpz7oyJDWX4S$9yMy(iKBn^#(DQk`eu-DgWh+6F2DQ$G(p?`vjCBe5^&n-* z50TC$s&6m!1w+sD!n`wP;h=(U-xnBFf^>!1mrz7hHrym8^+86}gudS`%Y;iB)G}xB z&Gtn+-}~lh9S({Bj)ZS}^;U(w-)puyJ#XFza<%cdnWe=TCordSx;*`_gF{e`dM7w) zaymUJbbqpuCu;A$MDzY9nf-PeYlVR4Xk>So{1YyuVLl9UC-`|RTeHZ#NRn)$ErFE$ z)b8ihn~_SeCL^>{x66-tLbR<;KTr{BF|?dpAtq{{aa1#aK2@V7T$sA1iky3M!n9$E z^^Na(S#z3)$+Z`#De!2wsH!l4n1B<7oFjCZsC=tXO&&Srv#tPy+cyR`- z*S^3;=VNI}!3^G&yR+eg zeY*SK{YC2XDBWfaOzmtG*0tn=bln4QVvnY@&|j@`L5prrFS0n|D}N32I6!9Nh?`HG z?X}Ze@G2;qOJV`~Z~s!zXz%3}yklo?OSz*4UE)-|+v@-;fu>0%lcA|&%kkbN=yc1o}Q z_1&N&EW4jo?KP25TO^{3!8ZD@+ub>g8hNY-k3sS})F^MbQA*R)x|(MfH76%=LRS&c zy|++~Fs38z4x`=T)_Q#dUe2JSW&mQ5EB7%Rku|QiFb&X_@Smg$u^@2~u2O&iddxoy z^wN`!G%lb&a$zMJHJZ>n*Of6g-g>uL-I_SkzT9UxzZNM?BEKfrn9wFg7C=ll!PSR{ZqL_qP3 zS3RJ|ezUgYRFhEwQo;M|botG$E3~?3+7oN|Li`bLWk&^VSTbzK-93{^gRLf5ABkQW zznz%c1A!JaGQ$ykHFtkYAn8~|Y#L-W+5S2$^j(`|Jjq`=B_%fKuH^v_P2Z%5;FNFI z(ohBaU;KFz&@7k0j_KT9uUgG7t$dJcs&I?dw-J_+?usVjTdV90;C^MjgP{A;V+Hk# z62lbCO=&Ciu$>{JO2PpXlY?-l#lgcLO4iIhFkuZ}W*b4bYBJFde4MDeLecDzHlB7` zm~peYdA$e-1R&OpT75=*q-e5oGSlHE*_hFjchdcxTmry%!4r?-WSz245_MO4E|;q= z;?Mvl+>hbuW0{D1LN&E*63^@50mYA)mR*+wr0rg>4BjtGB{k`~COG7XChQ(ZpYukw zx25lgzEp$Lh7W=PI>tMI>AXJhc}-gDP&{)3HEI80yDUg9R*rjRG_mI;$t?`Mj0j5D zbG^kLBlP`>B7~8pMxq)o6~z-@>%0Tu#O_7TUIGq0rOMj=yIX!cvmWyTKm>2uH?78f zpb5c(z>lKAo2{%e|*$NT=WBW&%*vYu_cGAQHSS(2ck!)Prx7 zdFny}1;=J_9H%s(=Io%8pl5w*Z9q*re%dCAi$ob`$IG3MrGwU-nS`*kz|p)?Uzr&5eaAZwCHol8j-M133dL@-1b{Y*h@#+Ie#?S zc!vB13OrzcF|Vd#QYAR-8LCgq)2WYQ>zyj3>~O7*8^b8>Xa88qM^LYoc1@~A*rUDE zDs{Qjjh2?4t%1lJqa!Qgl4&nJ0@RMnO11ATuJo9vwuaC?PWI?YBs`#kBdeT}sw5S9 zKsHa=N}ut2#IVQP++wA+EFsgL^1uwGFc?dH-Vb;mE>ubp+Z_aSzLI$=L?I}3xyq== zY|({b=q8SIlwopQr}bwQ**8P4eblZ6_T|exLW@*(2&IeOlQyA-YUSb+mZhdr;U^FM zCXURz&4Xcks@5as6wvG784zWKiS#2FgcDguP#!L6na$hhCrU>Vi_WzhXs_xZ7Kg!0 z8VAI+G(_SMRH_bd?p)(t&tf~y0`M~+EptZtX!8|bKSTX7eK+0V2+>a7M5fK#iL~td zS|iH*mFPRU`7r%)eRwM?FCU1X1=N?YbXNL z@l;HSBRs?$r9L(U6>qb~14KQ=I}oS}E&zvkYk`8#HN1pjX)qJM2m|0e!%>UUIws=GB3iPphTR%nIvVS>P7l+zheK8BAb8V)KaMYg+#L$u$kF zuSFJ-B>L5KF^4yYbL3mPRv>T?&qaLi6)=UCt>J!YQat;yo`{)KitZ~q%SKn=(ZNNe zBUQX-gFO6@k!Z_Fvt&5K$j-Dtalkw@%mp)uJ5Zp@6zW19sNfQzyg-68@OE8HAG4_L z!8SRo!~W~wUy{7~z+ms#Y@TKQRAcyt!`M&^G90JRPC%^7cGqYqr3LVOJH%KPFN!DR zrMN_?82<6SMl)|ebytX4R8wT?l|v<8s2t`kb-VC~pCl}gz?q*Iq=&>!#Mt$8I}c5s zdDr=PEg*_--OeeZL;XxEu_Zr*$<bl5t@w5U4m zWm+vOj@+?)SmQXdBQ^R2@T|&1uTh+};2g_ueoBL3>x3GtXE;W_8J{Wp^R zi8*jwyKANNNrWr&wn8$Qq;9cu-ejoAKJhD&^nnF%zOAr znKeFi9~!vh!8j#RlL2R3M-Lobc}`U7nHBWc8?e)(lbqq_X_jhi=couZ#L0^ZEd9=z!kWW5YHY5gtP~xnN+W)@=~U zx40$6{BT?AmcrWXdSB6&gf2PJCo(7|<-a;#XA@p?J`e$lV+#t*`HhRm=fm*_^E0v_ zCsMs4^)i(Q$xNya`Ycq^qy!*{VDEENtnnyp@SK&%t5F%w2{AJ1Bk}W7tP*|# z*)Ksq^rTd)c;TZkm?J8NtS^j3U|zAeihW?q^`GngXDtx4W$+qb1V3`uckVpDz{KhB z?mPW`n3BK;b9m3zJZw10|7b0+KI^WT-HI<16j7o~;ykv0gSlGAH*D#iajFpwJjADF?w4RVlcBY7e#a>JL zrp|b#`|ETsceF`K{F>7T4M?fV3@B!nlJi^$H2W~V^6P4Hak>(wI~EYmi^~SxV<8Z1 zJzeQHy03@feuK`Ww>?3Y{qe&vA9mYZ&jp`vn_TE38kb9+ZS_L)~XQv4PLqJj#__YQY9N~3) z4t4_z!xnBMlZ2%^Mg=p!A9#}Z;l+yfRES8QTp`1ytI!g}ISsH_%uRYC`9oM4d`g=9 z`bBuv?VI#9?{+^bc#-e5SOvUp&_31~i6(dk_G|%l1+e$ALn?f!hi~q|i|mZ=QY^{S z%#K5^Ud{)3R#k6i0VSoj1P|eRUT}41;?cs?Zx;xBn933bW+daKWWq!wqeeMy0ptgq z@2`&ki?Oo`$|G9ZHSQ4H-CaU(4elD;-7OFt1`F=)5Zv9}-Q6L$yW96?pI!f{b9rvM zX05rIs#!JD{nXoU!$EA@7o&Y3t;q=U(W4wg9ZEdEc%zX1#a`|ynrvzO0fR(#O9m(0 zP}FD3nBc;16pm;JAyp@5CqKmMc5DPzX1?$z@B*9F?kFq9iV}KW(-GIdxDVjjpTX;g zOc#hA2}cSYAgrtpHkm1}eH2vw#Nn*Poah%mi59AN38+^nrS8onKcc|QC0b%ieyhC; zE9*W6gB_L`FLrBWe7(ahgJjD=w)}@#m;Ln?#n(v z9`WYGgiB@Rw1eC0CCB^x=KiVP#J~}Sy_J5U!nyZ*(_8u}hR(5UN}P(AXHa(%ACft62&!N$VHOc#NDi7p7=18d?O~ zqq<)}H=YqP4M{L!8P$_Lmj`_=?V;%9-`Bf&d{8{pc2)h*m)qtesRc3`@~^QF8j6+$ zhD^@))0t{_x%Z$Im@Sa4q6dq=<#Y>D1$cxo>hqB9xl&j|mSnt^aZ@#g%Wl|+ZQr6t zSVOv|(oP%7n}<1 zZ$Q^_y3`XP9n94gzQ7{jm%{TK^ChG)%<2ScE>jsI|hIlx~) zL8&TGi?=rPI#>#=)1M>>|B_>sxP`2Q66ixrWh=~tx*<5Pq<{D+DEi03x4px!>_!#_ z9MD$Ee)%l;qED`V&hp=RFdAblt^N=qp%4LNFiHjCsu3gNQG8|6a2|wnxfOd^zgb2X zsIrb9zmU|XjpOcC`2}H`xtt6ON<}@K9*;0aDJd0EgJL#wzbw%7p-2Kj*1P6fiM$5NC-D_IvpUW0+3kjwnZMT?Go zhA0rOT@tTa<|He$2^30j$~gy>KydA&atGmeckjNnK`}>Fs(?i>r!~KW&O5zy2zZ*f zc!3vlKF%%2)wZAmkEwznw(Ua#c;Pzr% z*S#EBz_B9EvdhhBn~?0M?H}P&@W&$?kz^wc1h_ym=krOZ^ z$*fjg2j+dS#ckDWz6a$DKv(UlhJYEH9uUXfKV|SrT+$)st)z`Lp0khN5t|FV2z=YP zKvBKy{MAn8{nPt_20#}?@OFJc4u2Oubd^bR@4H6Z9~LsmLKy;G6m8zX3}#%vuK*t; zH>+|UO!YB@U8YX{t;{_!DlJA9(j!|o65T3Op!ZYIdyd9!B`WSH=<^CBDkbA$61haF z;MJPBKbzdKQ1E_KP7)ndpQAQobfa|M!j?NAmkh1_+HX({XnF*GK$hzJ6fPOCO8V26 zK7aDN1@0iRS4z8DKjPW6F3sOmgCL_oFWcY1AgG4{a*a+;2QP_i)VQ6@w0g5v#wD+0 zeUC|w+M%X0=)Bg-7Liygov9tE1Nf2PrlCkb#*pX4UeZ|IHL8;|q<9uc#U^WLkxLgM zWlDp1N9GJ5oSOp0l1%@2`sdm_4R+!4gM3)pWY#j^F@*6J7M9=1V$eoG?Dp_`@Sf2z zJ`HowzVlYTeqR>!eN86W#a?k0(YZ$o``^RwOngmH4@>(1a^@%W9pMIByRpB<{(7&QX2vO&B4e?>H3q~9C1NZtpCyN-dZ`yruUuiJQ}3n?tspLTlf&PZc@ z&ym{LUFIEZ=4NMb4E~{Qul^AZw|z8c5PvSD-Uwqj_T({Iwel_$!REGV4`gF8*mI5Y z*QfAfjg3N|zjwkS$?3h`%o~7C9~khqB6if%XF%OxBs2S;Ir!sXD4#hyAo%|c3H+CI z35J)@z`vce524t~#;UTpNn|KaA&|3;Njv&=cTN&W@`AJ<)Gymuo>NJFaVjaU+w!Y= z71omYyL_bA!4)-8ywJ_s$#O?(ehE&OvX;l*76hdS_wkMO({7XeDJP7BOE2f(sD_7X zQrpSB@pV`nna4QxukqAn$T+Tr5&~u|JW3dVlJ;_?NOGcP-(~YLiuYFn&l1r#qL1-Y z$?T;H7rZ8|hh#`wK3A-U&}exDdV9x+?^H?fYZ!+2k)&FRUiCMf`q_tAZ5xk4eY=-E zA$5smpP!ZW1XH{?CO^~a`EpliTSr!zbPLIsSK9Wjsn^{(UYTKj;+nO`=sJBED-n(Z zt?dfin;00fRkp74M`yAx*h$DBqAM7h)#$1p?@t`e%=_{gU0f}vV`yspcl4%p6kX1} z#*9~6W9?of(U~3f$!eHZk1nU)A@_uhq8d&0NR%`$$?M zAqlZ%v&hDKlbu^G@PsgcyE>1pN2Dwb8=g9y^$9O&iMy-Re6_7pE&gCL z?d-oMpol1%sJah*1)xmt_oN%CWufI{|7e$y4=Ee;IPL4=G zjbKEEKVNb_)W6*RQ=A6Alk9^?|EW)fMnK;`|Ib!`_uv?)=Fv88IfhRo<%H{bzJ_n66f=D|5l|B<*FBt1)N|YIo#*bQuvsmk~dY7G;2>r8O1&oB9Tv)#a^b?2B zi2iD*+6Iw)*4`Gj(UmZQfa{^wvFFs5bhd92qJPO}#`2$Rb|XkTRyAY_1P&B*ME=#f zCQiv81Ka`ltG}@B$Xm8lm%Z*EY&afmLhvGv79X-->~HHl`5rjDgnTxA`zffM>a^#l z2*$Q2xe|pwOnz$wjI?CF-VSxfVCk4(=@#r4b(vBmH)8q1d)wrqLO8SZmz12 z(^2YD^hvVcK23UmC<)YAXt`-!2V_rmplA`LSiKs@$j@k8Su0Lt7h9Pak!6d>5pAld zPAauFJr~R%f2_SUni9O5m2wURmbi@-LvlgJF}U-6$|_F*;j67~N1tJjwR$3V-FpfK zML$@hfQf*B+{PPWQ`7fn4KyyOR(u*%mcl%a`q4jI;1Kl-5TBVTAX2Yh z2iYLzFwPf|PuF;*(Y}R|bTp)K-?|>rs z9fSOM+jl>zTba&JZmz_8!2XB&%1zV!?8OK**@8T9MCYw7YE?OfR36I1lN5YTVUU0* z?g`Wi`VL{#mO;HD&vI*1Oc4r`zM!!LubH0`A;pD`n@?Q{Okc?%b2 zpVzY+qi!G!XN38xU!~1R-dBnNFYpiJt%45J8vF5qBMr3touJa_v2;4I_aveY5^0%H z89tzQ2ILYvo_!SWP#jZS3^L2^cL`$PR}VLPmYhuA!`J`?NKA4r#(Z?3mEB0i)v>fc zudXHA1MkbgQRSXidTTfFs==4nGoBA0y3^|FwdM6CyAQ;M&p|sMFPplh)i_FRi|)_2 z@^j1MMmp~d zKrB5lsSY3$Lzb$4z9mgq=fL#fCyty)Z{q()cv33jFYk`HEFL?IE-!SfOf|yph=*VZ zt`QC82R#>exp)ojP~ytc0GNmGYbKmYjI7gc@v4vLB9^@5!Ce< zoAVI-6HC9IhD4Ek%7o@)&ke5eAew`2LP$Qx(qFhug9}rgj>!K2;xWP~Z@_Ae8|SCC zCoPHt*7y|mcW?K{$IY9ljkKh`@E(2A?cz)P+SgUNj;< z@JVi%LqIF~zpmy2LJXN@cNvOadsueTI<3Td*9Ox=dw&DEI4ZY zo1i5y6HlRirKhJ?pNU|jWYE673gvsq^x5zJi>>$2X4_Q=U_&%|x}6_%JF}4Rn7BF* zzO)EVGyQa3!t?1eGi=YZuu9U66PwC%{=|TkgOvf>7WcSsoVHoj9{UA(?7V01w9Vr= zDBDE!EdR)KYrJHn@5Ydm3vKexTv=rLJYAP+;|6CaWOm?If5L4CS*M=dl{Y;pA;KP3 z-^k_Vw0JfJ(CA3gAoTdZhoE?=V%HdJyq?5 z@YMdweoeayV)$S@`*sqQ>ySs*vTAj#K8f`Wd|UtHn|EWA{nmg<^@{+yez+GH=we&5 z=TT88tjVATeM0cvF^+f$ z%p_Xydm;Qb!k*|z^Umd+bGmXax#2zUL}1um3sWL1z#JOQ8?OHt;$|D>d-_GtZMC33 zb~#FCNvoMgKml)g!|kQjYq^zB-^J%*WPBBNVE}%dl#ng{sh(#)fr#_Q*2gevMUoS% z`kCqsIQPCpX+Ot(RP@wy!TpTD!(MtVJ`-`=Mg9a98^_izgIArZ*DscAbQ~M&np)Ls znIF5yhB=ojiZk&McB|IbqUGIixic(VyP9g7tn@MBR~6+@0tq;mPHongGQ(dm6dfuq z))Zq_Q9huSIhS3}+0MkETc%c)wXvpGJf(i^*#z~}1n zn*eK4kq(5xpVS(iwlb`xk1V9q>uhavX?N0N5{ zHZBbw(|v6!ALsdNG2}|Q*=pX#HRHC!gLlNiUFv0RyZG~W z;K!q1osSX5qF!TlbXzk;(H3wVIbD?rSPZ_gpC-YhFtiFnN0;NOyvv*bh$+mF ztWaXe_)d`#$jO;NtzPh3zSy6q8;>{9IV$Y0P!t~VPa!@|Tj`;|gx zX}m?Cuxcg0|8%qCM>`h3V2*jpX0MQa9SBhwk?UKfFL$l|bMZcGPi=H{Gh7y009`L}okliE7qSQXQm>WCkCu3EtJn`HG;$zl8wE@I=LuKa8{6}>>8 zEsdlQ;~etcLmzn-s{zTxJaq94q%=|N0!ZfASHE+*`YvhiiNB3_+r_bD0WxFZF^_}4 zc2e$QYW6(*R~w+>rFCK=$TU^mJ?okAI1R0Laq|JRJvyi-HiB>6mZt%knJ=%~hZ38m zpmx1=glCp7`5-H*$~J@_#s^1OoyQF2@ob-*hG51*)u!94e?MVaG8jHBaH3r~zl7{* zLo+1^4GGFjFg)5FOlC;ruWJfK5m-$K`pT)p#yzJ}mlHVU~1>E4FxC0C4Do_B=lYyUmLH}i4Nrk!c{ z9Hth>%3*MaFDbwz-H2yp!BENlr+=KT2^=3wv{tiw26ncrl1VUfCNq$^m(HIphd*`EBPV2|+%22R~0E#)99z z0EPOMCZ7h?>Z0${=r^EMaX*Dc+yvM@&&d1763ue{xx*%6E5AIFR``Lvs$6)a}qL)*S=M~jV@U3ru1 z117s{uUW1bZdv8$MId_^|S@TMh;@f!yGxPgI)$5k& zn@1(;nNXLlLPZW#)r%EF*0qD%P1q*C)D-Kflu{`q_P6u?p6%8d9;d@<>gFL4t9#8) zc;oZI@F?^zuATi|6SJj{%aLWei4pMG|El8)pmHf-8-}MC}%h z{0skt(IP!~7*xEUvAbUf|nDmF3|P z^-HF1{-vJjM(1`Y!D~4-v3%{!EC`1b5_=I?`)A2qd0_}L5PV??_4^KmL~e)*9tejk zCM*KgK<&XL5#gPI9u?G3^EZ#D=LQL!K`QsK!@569ayV<=}Vz#N)9+o4Jv?9@O#3glEt-_+e35jk@trv!5s6QbsElv=c zNX2^J^Bmre%nms7$49Tp%@#aBnuNEWJqDc*^M&b3$sFtnJUTCf+!1JOQ6lIDa!H-} zSD$mBN5G)pyX!6n0MHWWSud!a@*S{7%e4pt%hyHgF}}aU0N0)m1?xGN9NkF!;7(7i z#-D(`so>8Ok{Q1N!jV0n)u?>3zkct9LSz>;y!W3Ks(;TJI^K_LwUcnJ1;ZQw+1v3v ze`y?(=mFftKC3mlBT)@QKFz-zg}i?a=y`@d5*E0g$b_5(&cJ+(pxQl%;WKEVcAD@V zg#3b`Q5q{pcIziIpxSGGc$Yo0Z&(Qho=mMw>jp-GwCxp5T**qKQ#dRAD2u#Qn?_>K*_JfZA41n&%0D-ucrwB=mqV7DobWWB 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ġ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 delta 126035 zcmaf)LvW@Ikbq;`w(UtK6WjL06Wh)k+n#V@+qP}n&Lo*Q`S#!Ap0*Et=u=l$b=Ol* zRk!!UNAV6U=SP>t1jsw*U6V@<9gIQ(v)41*#&;9ZTChx4 zR}WdVqPlumC<*8+I-IeYw2JUk*wn_2@gNWO-ZnTYwh7?)L#oylwI@}f$sJA4gY zI!G0~ItRd7Jl1HJef`T8D*gU61`^mB;7p7-#c>`I;mb2z7X-MX3Y9H=55)Ji{z3Uy zH)LAvIdY97$3no8bTEn{R-rMf@Ne#Vwz-q$$I;qDCWn&jVqhwnFg=I+8@ZRJXX;Zw zSt`$W7DNKly4HI^`(CPs~s?&1658k62^T2EssLVG^g)U+`nHe zLQ+#6>+f%-_Miu(i^B&k6|K`Xj2lWZa#fR{lmhdBm$)I~71r$#;$Hm6XyH#L4Z7+E zC?t{II38lTZ2N(t;@W?e{R_)zmb1mPPC^To$35cro}Y>KIDtsIZyUBv!Lu#@2nyFp z3V}~bl`|KJxj(JCp|}&DdHvg@w_mkYHTE6vw>Q0=AXIPAyI|btEi3dktDH~SqBBAW zoF$tg44UdK=>EdmORX1lNjQ1)CA9m_N=nSTbWhuR21xF*yzi~j%uyTlxqgF@OXwbJ zF!jREmThLD8fveC6w4i1I1Fmu2U~Md1MWr^j=%b*V^v~`WJQ0pdH)M!;VUX%*b}y} z#_O*9R{PICy@;v3Yj@LXP~mhcQ%8=G?40t^uMkr{S`IAFA6v3t*szK^G0-gZdsi0_ zdTVfXsD>53ZPA75MH3y;Nmz#vVXqF3dl)mPA2+2mpD1xKM}~)`r(1B|4~m)_0)~U` zvKpC}rTo{VB#c+k)RE>eFc($Ez^iaCFrv1?P!TpnG77==YbXVJ6*cMVzt1Yx#*`9K zxvNa!HH<@jS_*dw`5|SBTin$Nwuvl8LhQlYg$X2(^~2o=ak9AdCr?IL72zC0E+E!Z zoAYb+Rjb^v{t0(bsTaPnIM;tc28_ijuYt`_Q;AF{HTVQeRDOLhLh*&oXYy-`?tD(P zAY(Zl>?r7C_tYZRDv|}8?%~;yO+2uawOWkHjfP+7>04!YC>Fl`gkD1~J6k~(y9@!P zBnp4opuJyAwOM*M)vY692}6Rtv()!x(hd&9{|+#TurPG3AX{B!iLtqm0wF$l`{>LQ zD%BX)?ge~~;2Iw(KQ51C2D4?j=QV=6XTK?hZAWY@iK)SVCq_bx(18dba6ey2T{Dnu zk@AMw2%-~~f>3oM>W2C&?A9NztciaO4(E;Pin_*!jaVQ){{{x)AxY#0CZqL)D-px^ zm9rHv<@Ckjuf?Ik%OSzC06VTX`a7%!)sJ9S2rp6T9}{rq_Zojp28W#TS?h+DPxQ&W z`-@k!@@|?rVuSRh3iNyL`#~bcObKigHv^)y|ME)$@7@WzR}?Uz@bx&Jt_PwcTO`@W z6ZOyv$!`6cst z&?6g0dUTJv0e>;r!Avwd^|Na)@OAG9a|lHRv9A=omhZJn8IZy9|AVpsL5)F=Cv%-a zEJpUXA8EhOA+Y^5s;4e(LLE(cKuA01Ou(zjV#!SS3Y~bV5IhzYzit=Y?F(_TDlSXF zfW>=~jWXb#)|l6GH9X8d1PYF+CLw3Y3@-CI8Y_zR-W&&bQQUw@O`)in3X($1j9NE! zAzJJRv3{+r1GbA?QgHolQts@V4*58oKRK2+``wgh zLUZ3A;cWV9leELSY>Z^7B=uqR-7#y$t*scM`v>G1k~Q({#C_0S;a~0F9@sU1FR0zC zOtw#i30n+tuMFvxGgeJ>sIF?^`WhR5=|vDnoe2-VF~sF}%kwVZTWtU{d&qQ$pBARW zb)YS6J(kXA(&w+k9U%AdU5Qx3*TFBjNSjIRh$LuBEWrnk;E?>x3oHO3HF!cQFH$n^ z!&I~36IDQUkquhD6{NSLt;7BSs^S9)&AxoXN`L3dsr+_G1(807iukph1mAYxbtGn8 zfxIAt#3_B2+*y%l0QV4_BZtaGuBL~qK(rmL$$fU#|KhsYtWLTEzo=rm_&bnbJu9kC{LpZ$K; zh?5G*A#?2$|4BA<$swz*Lu^ERcyc4e`1znFPQ4&Ic+d<$J3_r~hp%LbyE7ZKFboC$ zW9itv2{W8`8>TRu0qa6y=YCXnq3B!-uzI!f9Ri_L$fSG`wku1`$z%Hbd*dBAdG&d zf;v8w`G9qy@14HS?>hgmqN*poMpJiDty8xr7e7HYq6V*<=Fwltj-g8)fkeI%q34!s z?GU@kA|Y|PwIWOkw^EEWsO?hKQ$d3}g{-o#{rSJ<;-nX#jFo;w<4>|5cPr1(ZbG6zK%n!~3ZCk;%RdwvYi$*%z_JRtC4>r%uP4JvQ#*R8uiCX6s3bct}*NF-p&1*wHpj zWm$6Q12j{RBFbcodQjXyT;Z^+3Z>b#>a4dDae@z_A8xh9(%f8JTz|ddgkGiFeO$`x z?SM)1(V{M+_J>BFAiHx$So{#r%-yijEX^ocb@_-2RZYoAFvIeDV=Y65Ca_>1Wb z>%v~9Fa9Cbn&MlpveBbBk!GJ_B(mc%hqxXCha*nmzW-?Wd#kA7L`nq&r3owFto>3c zZ?M#&M3&->qs2)n$cSUr+H>$u<2130;ekKgqv2*ieYHp~`+{33>|cIx0X=6mT-T*M zoR@7?=l$#bik)zaS_u4e2;}h6aCJ0(xON`arVEh<9;GZc0f@!r)YGJw`Z_qNK;(Nu+AD1$?na_o@d`EY}|?Hw0pg(u$@YLR~^Fx*ugFx9D&D~i?{>s-XifEN!v2Tqd2 z7v@5P4SR6zAuhwCy@@(0?Acu-DA_?Nq3Ut-2~@FjrcQycUooQT*lmtZl2>7GOlNf% zm&yfY7}s6AV1kd;4sUz3rewEYZrn8e-EJ!MuyGvwbLRmWtBp9kUVPiuR5|seo+UI?@o}?* z&VIYty0~VM#@S!Mj!Bw1T@;+YpNEXH#zur<2*zg<+^XVoCwH2X!rm3O4VST5d0o9x zopvw>!rZ4|B4^M5ZmM$0@YYDaNk|O2L6V;cQ6&--#_X0ZA}W?RAo-2fj((a&fOW<} z*?X?0PKlmgSK>-f#_%sxr~%0Wv2iaPtf>BDvenz8b^;B~8XT@=DdLWjP`ORwoK#oq z2d*XGBFlp{v;Aq@gT&0wAJJ##aK=JC?$ekBAu8m96Quna*Q?HNf9Jy}6e+P|asPam z22JuYaBUbLW&P3Y0gS*rzuvw@*C5Xs=8)a1unkH|sY{-JuN$;)C6O*;RZd+(64M^n zgk$}9`u)9p{Pd-GtKWdwU~Z!adAPf8GR4r2(BuJ@X ziR^RAB{F>5ufG0Btha^lwzB1TZG_&BQGvY}K2y&X3Ncbb8j#boZNMLn&>6(p#S+D! zk-Ecr`_RkgBSVrkfp)`Il^igPXe~NDJUZwgAH)&xDaLsTWOb|zpSqAw)~<{JP>|&m zjJ*S4c!wMzm_&4>o-B)H^z45sKlq|I|H3LbXk!Udl7-@a=kB-g>gVU^sf$R|ffSeD zW9%~xWIlZ*2ae?_pyrH^B^1f5vB#9z7MRyOTr&n-;i(c#;0dTM^a4d;!mJ}|IAe#0 z2It?5P7+KkX!N0C0>aoM5FTTE5csX$X`$ADfh6e7yHiqIsq-hJ2_=UsW!gb;Wl5Al z7Kynl)pESIgIOiSN7WMi#qNnb*|cTG7b{A2ExjQN05Ugd%wFVl@H8-1 zu(lv%7DUR9@X@@3OsEaeQ=vMhMZvoX0=4uT+O9lgJYT34dYBX6N?f#7Y)fpk;9~&6 zV4e4szx9rG9Q6fN= z8#)MFE08}i@lzg_c5{|xwmf(^T6agS>NQPG0ZL>!acdS4(5ZL*i0b!Z#T)7^JQWIN zHif&9{o0z-LLJix<@=N1{8m0 zDUDz3Z9%X9Y7PLe&(rS9JOzJbg?SO^rD@*EC*)&fhMg#fa|pqof+i}@9cl*O5wFA- z5Mz?NYyI_E=S-P9y7Xp#hjpb?s--8U1K&lHv(US@($s??&8{~c3R_QgsE~LlNUUig zNDzDrxBJsS zO61dOo>2U%0&NsOjEmTfBnc*O46Qz#6u4`R#he*C3>IaFD-oapD{19^zi>vG=bfwG zuRK=64_FCOOddGNTpcrxGXHuapwU20$1IP)BxSn_G|IdNmVOFsYwucneSm^MqYnLF zs||O44IrXJi|;M<;yf!ur^n1*=^Vs2W;!k1?Z)jXb?M7UF%CP9c(GLpry26*rW}n) z39`ep>i)Z*Z!7x8nhMUpUbY2NOzM;;f2?^<$M6X*s8#_|1NQumAa zN^#aA@>F4Ie1VK%ZTA*Z+L3IArmwdj=@<u+F4yFn+qx zGKx7&V6DtI#DXv!cbA+7augL^XSt?+(=sB@Ll_E)VvgX?30>R*Sh%uFZhu}Ct$UAQC;hchv`QYp$xXA|;i=NC_@Sn- z&nt4?T-x#XUl5t`;gZH{9;U>n&FOmRbnP8BX0k6?rjBZnl>>T$d8rYwJlb*mX1U+)i|tKK5Rc8$Tb z(bgpZ@g?<29J&{wGD|hGa+z4TT3eVKBhf~8RoRS^V;A%rfNvo$cYuntFDbmC8)z#* zGi?$(-`GE!ak(vnk|+IPa-E|3tG66NqC#vevw<`pOb^b&4ldVp$|gY7OZ$t&VWq5J zD5M?4b(q|Jk3SODKBf(B5`pCch4U$s^!mvrLGL-MAoQm*S>Y%<2j340rkb@K5YHQ=a_SRU75N)p3n5F3r9t!a z8|~W1;LXLEs~eg6Pc`0zu&#|vOj<*pJt|)f1tHb{mW})tG$ZA28G=6=Erz7k(87qg zUpOIAvPqH$&Y8}3^k9e(JTJ_-%s;A3s8Zzt?l;RkCsgrej^bev zn|F6c@(NZA@gsJ<DyjMU*;R>Esnj?kgliHoDuj7;x}M*o>-m~Bv64umcjL@nPB z9hf(O6V4iobo9J6exJj->g)$!7$o$-IW@(Vfq^FzJihxQWI>4#x_$q5zRLJxq%oYT zbjsO+C`p`?scM=8;TO{1|ACUnHl zxmI`3uC2X2->Fb4>M4krAB4}HEh;{Mxww&PKyRZxVH5|IoGLHm8{qpeH|%m^i49D8 zD?3uxV?NLG+|zt8{xWQ?c|Ux3LR$i3Gi?|z6YzSwdfkZ@`Z_=E1-*{u8Fc$U2tA?h z^t?|Rf=+>t+9w%UGAH+)_g7PZjVKl*I9}SQl*h^fnzw9O`Nq@QLh7$|D5X}dnUbSV zI*Lx`=O~0OwFo^hTqPYNe*H6)7kD2Pije3hO|R#(rTqDWD2h(zxCE9a21yrtbw@53 z+aU6>fc=v3OVQF?SPGg)PKOD>OUbw9M`)2ZyY;j89AJw3V9FMG%u5u2)oicAG#KFYwyp8}IA)$r-(boXl9(}Cm<%}k=`e^?G}EWYW#DkP zJkJP!)BZh|ttJwT_J(N&b{7mv%O6gGm|AAbwuyvc@992cRZ6ggq`V|OxZG&926nF$3D^#1nhQTA^y(EZc&j6VTEc_!o#>2av6 z4Xps}Fov9MvqfUQj9!q<=zcX_hdTS$Im15s{g~w9LUD2iv_|{{-HYU5Cv$j2^b0l9 za&FN9VaIR?8`3X1bT#8W_kOCKw$5!31Z75aC&3OUN<0RNWM_Q^9RR~@4LLs-17pm( zO)gGQz#--Q&vMBb9GhP(VQAfTMuZEz|B=?qq;}X+o<~qw7MtUQ(lvi;Zm{c>>`}r* ze~?$QMj>3{*DppYlAd@^)PvY|gDO4ie}RHEgLz~=rrsN*fhi&WK}z?oiTIBD0v zXo_t)sDk@~8I&8_`Gq~uq@X{BG#NFINfXr^t-?UB8Rp-C3>=gC9M%(|14bu)V}9Uz zMEhiOYo%GSB2CC@*JgYKU2uEnFsulFF0>;1qc?gGBki5Odb0|_wbnp-{~bY>HIOj~ z*#4{yvDh-YWjq3G0%|<|>%65I{|S_{4cftC@Xp5j?5@zv)(P+Ic@s+ermU=?G2vq& z=4@grl!gS*2ZkV9F7k11E>E|->S;m=brEfl zhIOzpwfs@oNkum+o=F^~cye+zzaMp}IDS}EsU%>c6WL*2pzh^yY*;uNk9+hh31!1u zQtfRfTuN4DzPEf}wZOJ}r8H9PCB_w-hCx>c+$s@RzJrusr`IikxCa*;&(L$>t8?&# z{Pb5w{KZEKeaS)0k*a_WQB3l!V^Ld zrXF#>o?)-0Z^y6uE(kkH9YZz%}j@vF{mZLNuc|GCT9^fht{6(_0@wuI>F7bc1L z$XhT1_`ti7-ARKvY5yqLUe5#`5>bT|+Gbmq16ld~_R5&n=B zV3yY{A`zW!$WbHdb1a2CPa8jeJ}2@3P(|w{Pfkn!QaPo((LeiMRQ@>Q&OvEzM6w`o zhF4y0G^jpdyzdfrLZ*#^Y;3u+>_2*A_dqTlw}1eeg}l$AoR=4;p`{1waZUs-+rOgb zV!ldQDfP~TM}{G7s)N=y+fn@>)h9+nW9i{5de&W*E>RYq5M2iQe1vy(G$9oLTRGK# zMrsiC4DoZn$m2wPxVG}eE!D64oUoe2+9|(29jPBKSedCkkS{E@j_nBgi1=2-7W&OR z$t6^|C$lWvV?X)pz5h~6o&GOZ`C;BX4T=A{1T7^p!@H{Uw|-!{5?V;IB%tR+)5xy) zQ6i`Dv5bXOZbzVuXD94T%GnZV8evLp=F?l`2$ViqpWwlpjyhj^)wx8(jYW?q$q?z6WH zH!2DN3&%P4%3In8u`y;xVcHkJNx9&>sa7SBY7E1NNTcV?_k*~efPNat*3qgG@Pj<_ zP4v(5Z7gvymE_(I*$iejGRjZRSc)heRYS`}CK+(U3f@mPdcVFrt!0-bbu2=|0sC8;kelbs&NYIJJ?oy`Qg6UPE zGbY&?8$Ec6U&Tn&JstuGHD;Fi&S?r>I#F0AiAY#$=cy>d?yHbC73+70j*niJdAu=o z4JTF6Sbo%!BD^?O=GIj(y5%}<8h$u3Bj1Mg2Q%PUzhA^A&?)2L@*AKVI3i-uU8^&- zH`gCdI!1At;bvCNz!A(#?PbFOyop$dY3oMEzbkC_)#@bP z@r*Ef1q$EpCtlpcRgIByx!f9VMS9+iza@d9dNNFb6gV_O?2_iCF^loE1w7GYhRasphYDB^FkrGaZRKx}J4ZW()40(gG~;}liC>Q~rDbidpbp@dKjJjAI5l86i~_%@)%)`C za-6`G(I*@2PcpNw``v6#tOjx^K@IIntRdyy5KjETG;cyaUNsUj!6`Ddm=%UL4DgEJ z!YY@5%VPgk7b@K8*upW0bQ6?#S=#Bp{J0fNMYVeh7R`w=L5REI2MrQBl5iM#cS!uG zUAFA_0)WeEG7#bykOs;VB8(XQH#?3Bk%wqq&H;ea!Yg+uL}dzvwn43*&L16S(v@F0 z2UiwUv?x%w?zF*j-K=}Tq%W=~M5C?hiQkKxv$gI0MP(Wyl??6**Y&AIglCMJq3pt6 zud*F=r6SjY;}j%hOdE^luf6@Z zm9Q&mpGI-F_3Mf!Yp2iH6%WE)JG;T&iZ7039y4x;(=Iff79M*j9Z$|x@wDn#>V!}P zEh!vLU-)19*d#2S8|t^#(cSbFHVzC(&-)`ID^DYmrape1f45J}T`K7bb?pp_17pK? zfpQqUF(M?<@?d;@9B3gbmlG9Ycn8mmg5KEx)~qB%b;1lyjxyogNIa(_h)u`H)fo0a ze|bzdZW5=AL#WSarp2$gQ{EfC(GF3I>d}%_{O6J~ownQuO?b!gH>pHU=2i8bAFa429rFLk*V`{Fw*{6^1sKpmu~V{G_f>M&&Jz(u?jTfi_?GqZ*$Wo`UU zbl=@)bI-oK#@%j?yCP>*pS)avpBp8&&tW$W{qn9~xePVa1^v=AZ|l1Xc0+`QK;_oU z^c|h2`l_l+_<I=GFP!#ZO}S zJovajc=9wjy>fE$BqkNCa&P({Lf|5#zqzqO#U6Y^zM!K|=C`CZ?XIEKLmA93&_s4rG=p8 z^VU2908oiImdp5+f~=RV0O zy7L#rj`HKF!KqL4?=^VIl+uaXq}z672aJ6G22DcZ=t`|QcL2}7;xgSO?Z0?{$%;Bb;fofN+7W(OrAaGnDI~Nk@*deKQPVsQ49yzV>=#JV!frk=2 zC{VPYCjdrQ>Ol$$YRGMb`=s#7n_TdDBdbhMbPVHQ92_BS=8V6g*g|iVQ zOyejT9rpQ3q*gjISykKpYZ+%EFNL_!tPgfQZAoFxQnjg}Q=-3E03XNCYO7l;sb9>| z7H?vs)BH*iru(cP+Aq31*1FjWJt1CzR~HMqW7OW|)1R6n^o0#yf277@JBZ*&4>4S5 zi-R6u=c1r_Y%A;*V-Y#o8RSaS88WT!oYs~69?~Kfd4`^_5E)NqO75>>c7Ed-Ik#^G*ak}7291$a^6H}aX%?r) zR6o3TQ8&CWQDpGKI=&GF-b0XTH|0m3hYO9BT(31>X!J-i3YjfFpt}fO?B~F^+`q5$=-JJP`M>M0+nSu^=oEjshwQYq7eA!=rmqVtC z#+M$c8Ns?3$&!Hkz49$!z%$Ui_(Spv1&JXjYoluU7!9g#+aFlhYlqhkPgQqnB&i=( z8}F1bD%2`0$7(Nx)qK17x0%jrAm{_!KOJ9cks9=_Z(8&|J)`V=zN~0RhN{$Sr&Ih` z5Lk?qZm{TlmSuTJDjafsVErq{^g2P=%;G9Eb+7H5z2v43-nUMy;V{TEp}OGCBN}jE z&H0xmT?#u=fHj;LXRFDj)U{@1Pqs_1L1MUAavSAr!!l?$OQzDckp5|{vHKCYTor$j zc82J1WUk`+X|g7Uj2#)QMW+WnaZo+*;9w6k+h3#<_gRnB6*gJBJL8Jh_G@50)AXs` z@|o;mpEuy8Zlg**u@6U&Dy$p#?P1`f*km|G*7n z#Pc&iIz9GsDhu^>!?j>;!1#u~1E$-!AP>$h(5>SKZH$FLY(^AZA-EN=?{Ww2Y2rWe zW>miX4x5X`Jek^nvpko~Ve=5Q*BU?_EkMuhip()!a|O~{d)L|oU0&*K8iT?=NuNNh z$)M72A8jy`Kp1-gWum={!J7-ef)mT@-TY({cKO#I=DOi!}dEKP7lA)pEBB^`@0VDDQ(xKA_`;8$5F@ zr~jHdH?G9)Pc9ff&D@|A5+5q7YAJ0S>EXLC4DJu(CM0KlY=EbDpYS|6PTJC=^APJ3 zEA}4CUQqb#koOd`AZ;jEv%yi+*%s!GPXQCTKc{`#_JP#1CsLld$H7%(syokf1D6w= zESj&}SYU2Bnu?}KF)xnAQBQ>5h~S?)*G^<+LONQEBN;DY#y_9SlY)Ca1nXxBMnHSx zHy!HuW~VLcCE!;bF%6D>2VTO6UZQZv+BWs4ts%?|ipY`vA?2`7b||;-xl!`)oQLOx z1<9db(zt>dSBaa<+nSRoN#DxO@o@mhlYVRr(#Iz^CB)2WrhAt|NhKR}dEyyxJPW#Z z1fl*PV+*R>1wFsU*HVDOwQ1r$da;3n-tGQ}-OfC&p9ll({6nB!B4llUkb|i9SRH8a z3l;RK`qvN|zbz81uJ|3mqyn@5(1N=`djcXr)*}FQzHoDtx_YIzH6LcSbsQAs<)-vI z47lgabnVLsmgF7^xb8D}7?+bcs^(bYjlV?!CuMZg?&G{6{@wMc-Y zm6{hoOZW2>6y~CgO z4gU2Bc38t?J49FgP4rxcQN7eTx>(&^Lc!YYSa{InR$Y&l*_;DW5=#w<0;AJ$Qo*}F zh6}L(rG8HUNl0gm+#S6Y1L)icj53-GN|y8IySn=$%0Q-sc@}M#HmSP^0Y|*8CNBM_ zJ6j}&M%vl0`+%+k$R0;e12~xGR*r?PsJ| z6;}#B0GI3oB31sxmW!7^3UebkqPZk6m+y#l1b07mxbY-(aMjpe0Lj6(ws-21pT3hO z^6c}QXvo6&OaYc=>x`Z|k#XyT7oV(4_aE8;f|pZ{`s1@k;ckp%OCxC7N=71ig{C~e z)WAA_a+@d2K8CS|6tg{#^oe)>im4emGvvza5eh+qjF0aTh|yf#+ZC$%x~0U|*~QJ1 zgUgX2N(s(@4>A1t1{C$zDh9%~DZ_OsegDl$wrb^&q(o0lXo4j0yB*!xaSPAg4F1Uy zdtd+NGQX_Jj7M;kWK14s+`z)6+!o4O%=gw zB$rOZVmF?Kq!X2|HE1VhCZewp!{#9<%o?;vbL@>tWLIr?CSX86GoJ%xEjxI57b4Lg zgH%;aisMo9+vJE39x@+KX!aO_)9YW9NoCP{lcXJIayCTzFuZX~9hqbVvC8HNs<~x; zOT^!0E52$rJ;unH)tZW-TRplO>AC#(aEEo z&vw#+2apomjPy4&22L9DKT#+uhb24}H-GX-N{w-3MRm3w45jBmYPeV!HECLXbmdDV z%ce2`xKxbOW@ECK?qg9aT8#`k^R<+C<0M_rf+_)2-m_@Sgflo3{ui<4V-ZeT%6AHH zDLvc#p6>_8@jl;oZoUNjR|~@`aKN!6`Ja zZ30zX5?(!}9Mn2_+1D^#Qi_T^hT(>0N3znvd1FI_dSB!o!VZcZu#S)XUQ{eO;7F$h z+?2KXF>GltvAWMQvCMds>h!4m%vE6roNkuagSFv4>_-jU*#&OfPL?*V5V6I+}Tj9KhoMKQynr#p)6XX8#T}ZgzD1-0v{ChyB2F-E@LO zfEAU zH^G5J=$p+7Wng~u)(^k@oq68}c9d2aOu!!MfK!%r;c=zE z$F{t}GMKsJZsbEt0)qTAk+$Kp%(I96E0Wl*(Kh zznfuLFfYJ#q=JNU$9)3%a0}Y4$)HaX1ue&G1q0Y&9K8bSy(Pd7t2H4rEUnS|wD|(k zriA8;vN4&H6@L>0h}s05QPfg@>o}mfFA{@gBnDH!Dwmn}z>TFRv;z(@VPfzFy4DLL zmJ;bd^j5{nop4M>_cyU3ABive{&h4e%k>M3NPm zwX?~zyL~k-^nr0vMhjD--kps1ixjDBax?0L*RtkJu;Bd$#CUMIZh*nI>{iG zASonCCuO~*>dtIoe!XNj{){H}YDI7Gb;=@s$d|NhH>TG0WNn-z-t6sFJFDSflzX6Q zbq(>4T;SrKRdENo=%c#kKNkcqPa>v}RGG(mA62={FL$Ws874!vHAnB)zH^-vrQkEu z>AoF|Pm)psIVKWLF4d`PSZg(5q7!2ja-Q11kA{E9T=C!#Qx*I`Y}D}Sw{l|+y!(3k z=7WQ{QNiWgN_a?>g}dQOh9&fmgV+9UF7j!&`(PdZr98MmReKmbDm(O1?&tJmXlai7 zOj54w6)#h-6Zr$!z)izJ(gGzKZVI;UO84jI>O0~D;EXSjBKYz!`Heg6ZYnuZgMn0O zW4keif^>mv+tROR@hO9(ZzJ4VtWQ{EOQHOL8_o^9o~2!$s8C%wZVcnl`>3*2A-G~8 zf4Ip~#=9wk#)>c{+FK)uZ;ShB<4uGW6ONoc&gp;> zJo|-OK;|9Bs8+=j@%K_`VPN{4zW&mndZbok$6xk!@k+XLB7oo0tf{z|6(+D|}s%=jNdSvD5} zuvB>ID_FraT|fPOOx$$fbqGEc>|h$2?+S_m`}89U3yI|KgcjQM$3+ut?_B? zmp>!ssY`pqSne-`VX5rih2F2%S>W@bx1d1ZPU}#;woLO;>=&*+TLr{#zu(&C_~1g7 z{p6QchIzLX7$`0Dc2jCYk!?o82`0%i09-LuDP99{L4jDuAGx=n-;ws|3A>N~j9+~M zXGDGun}AyVv9Pxs{7naw( z0=6tE(etRQ;Je^m$>M1JP6$Yz4}fO-#?ubF6m<^urJp*(Dq?c)Q^W(U3P}v$z{$un zRn#@BH%jQqh7lFOQL^R@=3bybS~!Y=L1%*T!;I@>)PPTYvS8YBU7J5t<0VMj(_HqH^ zU@8b-Z2}1ojKalsMmnth-=Ml#GS(#8q5m~LIr6MjQOxtB3$J9Q@x}avH25jNah^j? z+gA` z+CDJ=3kS!{;M->qwAL@65vba^n(gq@6ImX(g%L9I8@jgWr5q%Ar@j_=r?%**YDJ=L zbBH5mi2U%z@i`uoPtA(Vu~Du1BP&%Yp}`SBNQg&ing+=~n8-Wwn5#J;T|al%O}VP) zTjv;pJR;L!Rs18@r+Y`~dK;HiH*Q7_vT|AaXBLXjT8l+f#;pmfCBVAC|DHn2!|aZ} z2xC+o-V^F}h~eO?$iH9;w}5{S;LG1OLxQD`g0|PTTn~X39s=^O)0}CL@d$sw(P*Io z?o`)}TL9$DG-2Azl@XPQyGf=_Rx~2eSLKpmLcpXC$P=Q%e7) zG^jYLDjt(QdDzAOTf7LvaJNC>ZT+c&Wlf#&`QB7V@li?){K0VTAq;4o{M`VfcE3D z_Q9Z;7c~0s7HskXq!+6>1|oa`^?_i&gzi9rAk+^4*aJa=NdLD6aK8%ue%O|On}z-W zxx3g5j)T_McHllhImaMWPr7rdMBloE)1J-{ix5%N zF3NXLJ!M7kXrUP2zhApWWb#S$aOLU=lkmS&laZ{n@fH1PM#YV_1QE}4Nb5yF(KJqDWw zLarZR1?wd~wPCp)u5`~SoPNRfKH0GcC2JO717fG0&I5&V-+foZe0g~6_~GQmaN31w zdT2Yjvp)7l#{a3NlwPs_4QI;QM0E|9lbC55-2#a16bRt83tcVH$aw9=w#Xm=!uyT9 zgldz)3q(5!#fX8;ocv3^>@r8x^=d^03$YhS7+c~~XQRa>UAxv8UHDfNF+omS6@dy_ z&YNM@jJ7;SCYt94`+s7>^;t9UmOW^++5Wx;;ICJVW_%}!|7`d+RM6cDX_}H(dr9q~ zlZgu%0Ex*8a>C~QLAqu)gn5v?>0oX2}IG2GJ%3b$tPub{a1bHEK> zRr=AUyD`;*eS-)!OJ-cd#c>aszd7>o2GU_A@I`#X9e(b;%X<(vW9}d0l?bH9Lx-mV z)y#qP{(MHC3uSoMY(DljZ^L~V_Nu+jM*7=^VkXfCyHb$0iz{_;aKd9_+5tN@^z}88 z(W@(KP`F9k=X^)=5m?Lm^u-;FWV2|fs0FFNBvf}^U@U8QzX(OIs{^q?c?~>6E1kzL ztXb8Xi;6P=1cXzCZ4wFhpTV=0 zADrheWhh}GToVI`|5C$4T!9T$YThT{g^v`OIn<Vpzp$bZg$ofZm0D#zA*f!=CC@xz(f0}#Tpir(OVCfIF?gQ{So@H$kWdWn7o zt6<1^+oZ#c(z3%~aIYL6`iIq1%5=>I6myB9k=(CsxW%MCWq%4bdTtih_`WDx%u<*A zP&td&Cn{JPgLD&Y%d$lRYh1j@9%dU*$<{lG$^4Zo6g;lV6-{$Kec{36CnDwe@ff^V zEmFpz)M908hNSuM-xWohi;AF~B7!B5KVka#rmj zK{u>bfny>S{DM@Rwu_`lnBXfT{O zYWrQ_M3mKbg(7~|EDT3UpoTN$d!x>0noAT&CVM{DrBcW&JK96T0wKrj}It5;}WeO743bG zid_&bU>5qAy*gTDol~X4nIe!DHJJ?FNzP@5qhH$D#G{NUNEcX&BU-r$>)wvOV7YW* zX17s=1u?>ufXCTaPJvHHur@#6-VA#3wTWnFKh#GZI?}7=fnfwp+Ok5G0wPta;R?^m zb;%bJlP`T5^}1aJ8pYZdwh;`*TC*p_RT6nyA*0`Zy`4!xc?;Wo&9z4~q~N>GlO`lX zxUM)_8omw>S)tSEAlh^$uI)&Va#TjrD$Ml+FB$lY_O}{;rV39~^^Tzq)X~bnMgH7P z^Ktmkyax1Uw+7m~Tx~!60c>Myx#MrlM8LA=RuFo4Ts_fai$m}DmmGB?$#=Pfg+jGV z(r_2R(f$Ta#vFv&fV!9AuJbOL-+|m1HU`9W4OxLX%0Qq^AmgsBiv?CN8guvc#(Iyb zx|73XqKw4=sa;M({{vg&j+y?`_1ybbY#7>a=1IZoVA^OSwMN9F7g0m_3*PCB>G{}r znLN{dm2p_kXkEPGCc&oAL`=~qF~2UL>u$~Eh$g5AGnvM*1&Mx|G@F{svz+odN*5zOvgFO6bgVJ(`Sw6v)5(JJFd_W@dI^I~_5N>Vc3y4S266h}y z;1^c}?}iJoATL2*iX+5fW}?pQ!P~sO+-4ciLJvb>@;{BvYmqm(W_OdLq`^>N-}!!* zt%F)=j=-&lT@x8F9}wYb+fyuK7}n54E|C+0W{ydv$5IQp38#x!(2n8q(;l_EXr2c1 zSAc@pRkdSMsNcOGTFDLAI!lixNyb(4E~LSSb}hP%uQEa`WU{#VKTtqNvIy5iQGq`3 zqYc3`iojQV4!NjjC$ozDKcY`%Y(>R(NQoX9mr?Tc+PkF<1NcJ?A>MF4Lo!}$sm^zT ztPtf)LmL>h2x5a@Y|*8P+bI|{Np0J=CxFwau>9T`zpc^0q`~`|rsyRmTaos*7&v?6 zoA`yzTb-i1(3CBhhYFQ>p2J?~B*XB@10L&EDC`#JpsFPiY!RgRef@V_bexvOD77!% zb(Yb*n{n2AJ?S2@OvMSn%EyFPtW|i!K!DfIeB?HoaL}xLk&M@2Rh&UVG5#wI6wpmL zB^gskN9ZrHe@zMr!1-Ge-gxJCBufee|I0+fstlic0jwv5nHLZ;Uw*hQyV8g-L<+Te zsoHDvb|0dj&g+wKxL?2dql5hO)s0+|9tM(TW$Z=Q>gV#+IM^!@N^ zfUn2C-_4I+f^XzoIBvVb1y7Uv%4N?0mM3F@PfEi-6XUnyWX2dj7NK(gDjjud9WQwH z7jKz|{Hdd>;5Aw|iLVV|SW^FIhh!rhw&U&syUon)YXRO7CPsIXf%lg^_ zUyr+=vBkB(CaD)Xaemci5kh5V@o;>c@(LZc`J|8HSY_rYz)9^Jb zdo>$+B@z<1ahtaib_4cgf;C|YwRFcqFgmt1hVnFzK21%J6EJqaq+5779xKYfZ#Q`$ zY0ih#D+R5(-*($K2Ys*{f&Sm+()?d$4i`C_@AT>oYTSi3I^NOJNQa59`q)GegvI-;Y`eh|UL^+_c?gbw zO-uUF*Pu4l?0*y+^l+Fu?{k6ko6sg{-QP-jfRw3V&a!FO+HRKnY>`Y1|CD#wgo$autdI#**YNWGbpKegZF!YbahPD zbT?Ykw=u<>wpf{h9^=81G{J0)T<*5cz>=BYdF87CQcHNjZNuW(Ao#KLp^zuPTI4xa z3vsKL;y>Uzic~1j%d^%a+WNNlxRCOK1 zu*&`)4tBNeF7p(Pd4mhf!p!v_Y?K%_ZAGcRy~EOP)l+1E4t3EIRqhaz z?6}kj(=Vm9@q5Z)la877=K9107YVhL^ANQ5BaldYSq*miWcSNb_URA;jucA+Mw}B) zN$_K4^5y*72n)nB0u-erUxw7^3d`mf^;cG0xAz~M4h6AmcB@ITbjYuZ|?wGy>BeH&P3U{`W9}z=vUA}>-B&wuMv`A_O#`iG zcmeQ}*ICp&WCEe|I477`E(Azpl@O1gy}QK#)uZwg(LC9kL(-l;melw|oUv+I&_inX8?-ux zj>Yar>AHe27-%6@Jw#%Sj%>WcV)&2?v@gki*Mx6kz<=4z_d7L^G6 zr>2l($r?pVpp5ml{){J}QZvXQ(o5!NjvY?aH@#PPCNKWYt?B$!227MRL3@pH zhkZl~Uly@)TH-Tm6Q%6;!7(HP6qma^i(-$xN{~N6Pbd6;&OL>fEK_)Rw;$(#RW@HVm7Hs+!$gq?9TodF6+|_`f5-^>O zBllz#hIk+@+tJ|H9isRXhA~f;3BlO5#L#R5Gvk!=Iv01K@ z-U80zp7>luQmbo3Y&V^;51*3Oe;ZE+LGL z+`Iyy4*?s$d0r5AbMQOxU+Krq@n;gnDk`O_tIfq0P@DrPw4vAA^rwW;2(q!a3%dnP z<$|c4<{;eV63bz)7nIKtqJt{+JyR>{lJD*y_1svY3UoG}e%ajO5FeLrrR^`Hys*}U z^>0+8+|8;RI>}QT*@kg&l9dQ2-Vyj8n0^gpL;ZN!V*(tMn1Y5eqKFKtk5j2o620$Q z{n*`bzo`GDPhwuDRsA_M&QaexKp!+IrtxOtw@tG|Aq0`*kPwzefP#g})bl;u2tE^v zxB~(bi%iu=Y=+cB%}^mCuY)>y@9DBWE7`;RO8vC&r;@+F5acrFfND^BhEu4t6%p5W zkDrbq-OIoL0Y#Lt{)`q6Sm1c1ENcxP-!tCN=iTo{;e=eHEf2O|gVT*^2}E?eWvZi9 zmap971Vow2(EmpD8E%JLJ|VUhv&TOOPHzj8Exf6-cPNIlOm1it+E|}98N*BkrHhPV z|FaZ57Q|W5a<7x}`^~~qb~)8JPo2uE3pHTcDq;q#EhUi=EaqsdGfy(J&&064ZrQ#d z@Q5-ZWwYxgw6R^43Pm@1smHXjiOUDmgy<7;D8Wg{QqOYzDTORU12ubZ-^%okHU2w} zyFv0?ZXtC~Cj5rAYgyk1!C+N?1>s^v{D4HKz{s#%(BLYx8jE}~Gjd|8IS^pdv@6A6 zC3FEmp4nW6adL1KVzm9tXbTTe(fs-O$%jES@??@q`uFh=b~K)uhP^B&+33N@K(k!T z!k$qRHzRN0YbR*r&f@KY`)hdd7qT4o&A@_1>Tn#m(3ipii7u;_>r}8LqaKj|)eDyJ zC;5giFK17FaaC@ur)tjpZO@`#FMA?<$;K174jdfR`>l7==P%WWrct<1Yx@^)?ic5qYoyv?^ z{As;L6SPa@Fof_$a+A2kB5Bq(8wdH~+W`N%QP#!2;~4#ocVB2ZF=un4rN6o_hj}xw zW8yZW>Qnd(XEJad(gA`7O~qlL-sRL!M6`|JDeE6eca1-nzb|}vedRSoe`}%sUjEnX zN)tmwihw*ciJLV`NIWLRFpSUs=#kS)+See79*VUp9!ZW39g}w}y+275@^@O$bmw-% zB6LOEQ6P1Ym_JR#Vol>?*tL<#eR~lAJNi9mJC42XirAKxc)O%zdF9dR#qLIYP?I(& zXBvd3j@yN2oZ(8}xDYfs?)Y_WoMb-`z<q|~pGgsGRj;K-KTF2}Yppb84#}!S zzM^|*vi{|0dl%*oO_v(h1Oqw8kBJn|FG*csy4WsRM$Wo7ljjZ%QFHSWcCosFKctKR zk=9%nzwZ0~Ew?ti&ja7B-{3M(Pu;a~_BXzo$A@J4Lf>z;LsqZs&}OL7;hy|Ek;EH+ zyHb|9auuak+Wg37`&du~Sj(G0X%^YQq}w(bt=)sV({CxmZ&9L>?cENP9CF#pA6nrW zblzOrs#|7mJasdT!UF90cl+wRb2d)BU!WBw`9}DGtF7>9qw}k)qu%d;^HKu@kqgF+ ze|6Fz8V+tY?yc6j7F*-bpKIATXe?WL&(i(cQyyEOwrxT|FcXdSfq19NIEnP3L?OvE zOxX@ihT}m+F!?im%C#9 zb^R|w`KSTZ?Rt^>O~ivhSm!*&Nx+9Jc?E{R#G9WbEa6>aOU31G|HFxJZslhOLL(>K z^V7fP1)VXWgd?BxrSfoiX8-#A)j*=T=Nf{YM7owS3^I>qcyM>y;)VidV*no!6gfLw zcERn-8qSVhHG$OLf`}>gYg0&!xZgULCjg?QZR;y-BkEt__Jd#SQg_n#UeiY2@R~CgdH849xrEmNdDMzNmFr>B342C-<@sm zAoE`ABu;coSfq-Jr$QLjjdl<<+<$2_&+SQGqAkNiZDD`0VmfyPy zb^6-9@#VPpxwC0InW=ieJ>_?V>0I*df3XOkmNY!Pm3rbLVR#mu_3FA`U{KQx-i$+7 zrE7WfvS>bJuwYxg4U0$8M zDI|-A!SWW*^IWEoo#s#hlFvY<;#=Ni3>xT?c^ITp8tiR#PpQ5se27Cr%5@4f!1ih& zBl8fJhI^Jv=KVaF(R@oXes*yKv25e9lr@;CtJjEk$y(jwc*pjgkvN;2|jvx zKG@Ww&gl2MMXENBO?9|WFP==NX^+^ckyek%Bdmmubq(>qBsLBY zi>rl)rp(%*#veC3zq5HF3nLm4DhCF);JtS1Zh@x?gPKWErT;f}0r)@Mh3z*G{UPWW z%Cs|2Iw{V^3${*8-kV|ztWI|dgjn#=l>G|wrdR@PuHS;DExjul-}1~4&y@t1x>ni` zbri+K1mY$sdslEM6lxyy2}k4cm7r-z>bD<6Ug3GKuj(~-l|h|-8fFaArEr75Ue@|e zq#@G1UInoFt8L|*oMpL^f|8ZB;6a^p!}vyXfzcWn z#Rv3!L%T=eW|m%3 zXivTkl|2~cV_nV54)_@7U;KD{=dXm#yzKF0yD}iBTS4o6cr^m=o7T2rZ zKt9MI?nAe(+xD#z6}HDJZa<~<_Z!LNf%kX;=*%G>P)43~@oiSJQ#? zXdKwiNxUiGJ>q_%KgXqjPGa{)^qZcct{J(N#JvijOCDxsdyo^qaZZRy3?Teh_+8Lh zT8n$=Ax4Ocb-4UriNS$$3;G}2KO|(O?w0;f9Lr4D{VlHKa3=l?FL_x!kf|l0)zj79 z4~iL(2ng~W733%Q08z zbiGQRz^sLcJnKY{O%iOiqswM31nzmj`$}Usq+oD$M;DD`h7kL`vg`am?mma4V&1n+ zrkMbSGtjvd#~J89W^WMmImlwm;D(~3YyAzVS!P?z%KHPaUrM~y~1Sk<#puIkWB zGr}BvsSYozac+DQ>;tirIbh)Gd@cy(1xI6#7-@{5iOT~}Zl3g`_zV@RUGSw0M_|Ds z^prF!pSbVX%vxUf-aSL(HcwLHAxeZQ0R5|>Sy5^knC4U)y574Jc!wfcP1$rm8O|UR zqD`(trvSNYQhuMk^XSH?Py;N6__#Ur{gA8_@aHL82*MF{c~~c?fPN7w12J1!-eGZ0 zUH|3afO_d%>I^ReguMx7VjG^GU6(&xN&TU-LnZnf!7YlgD9*BF8zw`3R!=!Qb%&L@ zU1ze4-6rbfL{ug|2WZ0c-foyi=|hwWeito%LBP6cu7Z%+Gh$1_2Yl?<>|R}^7OFLb zSo!*WKZR(+Tz7cdFT#OQ|o-T~)-vW0#k0gg!y%xDbGb3L`1|nNt)K2J zKQ=Ls;>TV6uPqra1@O_DSu^7YoF2MhH%NA#3+pt&r|51So^*;k#SYuBuwx*=*D z+KB7l@K+DeR@q8Y=pXmI8}7W5RKk{t#Q@Z_-lj{DIyW;{Vbz{6$F%u1EYy#FKCLPX-+*MmIr+8UbyI+Ul4yEGw zg=+W$`!GdeQkzKg30E|je0;HK21&t@HdxB{mU^m|*;y=WlB@dAnTDO!@iGr6Isqt_ zU~#gf$}xfcoxaFCJ-vTBR2rB(I2=n~XlME`s+96?=OhlkH!P&@{gtKVq@XRCW>tc4 z#hVw%{^{c38bjkGvh)z!V7M^S)W~|~m76Zg?VuR++^7}D9Ia)*T}gGZ6dw*5H1Tcd z!OkJFS6mkrqp3LZi+@MueX);n>pQSgX)m}Umk#ENKrF8F5UHpkz}Hql*^N%@jSoq)!GCdy%m0%kRv^K0P6ZweHa#rvZf2W2iU;0P;qk9|H@g}s2}C7Q8lGmIhG)cU?Ip& zDQ-wspOb&<99?e;u-+6&V_VblO65pMzZT8j9Yf*srJ+wCxcn)s*b=+tFQ_QZ2X!`~ zY$4o$&i}c`gJ7S5|6P(I2dR?s2N8hH%6CvN5<0RHvjg?pFa)R?8SR1p0~B*^2m(Cz zLA(V@mn6wvYHts|LR%;@-fY)ZdiIx-r(~<)$3Lf|B$v+GsoDEK*^qU6i_{cUKA!Mp zI4&D8eErTG_ca_P+MaqD6iJn^DyaAd(9&$9qdbYCEeCwaSgoEZO@-nU^FTaQ-fTKd zl<^2rv6Zz#J8>_+-&4A_dR?>qvs{JNB0-J0WL_*nYNcghOh`LC8)H~p8*8nQMD;D?_;48lGqU=mr34w&9@)&((~>H?mcp) zwk742dYe0Ltwd#Rvk>d~?11%`D+hC9vkDQO_)T>!DqO16RrJSK%?_ryH|)yhmWYUi zItwp@S<2=`-Q&{-HLI9v!HhXU7)C+$>FUjxWa$*+SvMb@XyF5ZW7AQTY7nz^~t?8UEqI zb$-9N6-l9eGl8&6g`**8}rb1V0$ zbv?_wBsbP4C(gKu&4KL;@Vf!$-0DyuHscMwX(>O=;+V4E50-^9KfJ3~N8w4^x>I+i za05Ev?0B})Ge%p!*q-?s9TF1HcR&gLo;{3mA?J4Fj4ww&mdGF)WzURR1ayadCfq>n zk9a@FwaURC%K(0vZ4$obLx2dY-Nxtk?(F=Zj!VfE z)$3c$UT{l4r`Cb|++et~8)VJ7Q0Nz|&MfPqpB&|ar%E1?dUlLRRS^ln;A|2|A`0ky z;1KgV^Dk<#{Jt=Brm@MxDWkC^BV)m3g59n&O>9jIyI=kJ_ys4akLjW%Q|Fr#9UbR9 zl5(p?cx5JW_RkuCoi(|Zlet$A@|C~FQ};bG&Bw5JIOxk$cMk~qgXIbc1R=f|Y=JJQ zK{Q@n1{mYb*>BT50ToU`h@W@cNq;o2udKkmZGIXVdC0hwrRQO1KUiL9!zE`uYUXfr z`php(Lndv$j?Hl$=ONiee25ZizqE_*8ikN3{DQY~bOYdg>f1}pdBjWYlTOC8H0XEX z#oTlYnLEo=Q^Gm_{=jitt_VpvJ}by1P)`y}`ZpZVRP|n)+Usq)LOL{x%~Uo%cfipj z1K0gE6K&qzNhhw&v#=()8z*fxxb)b$^#W`_#kMxTsJ7Ch}6|Vt#e#MDe$eS1MRgpaY+eDI}5A_L^)x|e9NtVDn`Jxy$VnvWY1mm2n z&%bpCrFyxe$ugDeHXW`QwX48kfDQY*-JdMK?^%E%%O;V4-2!8U1nwBh`={LF+p)1S z`|&P-7x=JLPASzz0T`LhTs7h&Ys^6|1epvxwq-Nbd8Ox@RZ)Z5!8b0g-!}gZxzB)L z{>N@XfN=h^f$Ksb4+Q+kvs=rX1l%6RSNw;~)6<@d*6X~?Kc82v-W^hbW9+T`ps`|n z4r(FKukkw=>Q9JGsG9>yOZ%HrcoIn@idx`Ct7tz72A(T^ZRiql1cI#H6Wq3DenP&Y z%HZEl{o>B8s{3xEj4!)QUkbkJb$5=xIMjs}?oeU456iR}Ov)asnw~(C&ty{LnoUI)7XphUnd6neeWxXgn47vO zS8W2TJlT~`>lBWZylsm1Y{O5I8~`a?(f6u@VqMwM;-xS07tF*jy^CX(^IAV;VD!l% z9E4ko@$?Wyr)Qc&9I-pdM5T=qT^&s_;!sz>L9SOeIDT60p-TV@MBDm~jQ@phjz&e3 zBpsLbn-J?$cc0kj&g$Byh|&oMFP`GZ{<(YW#T9)46c~y7N^v=d&y9$E^?yC@JX$?M z-Q}{~wUii8c*}@&v`x~7Nsx?)m9+f*8w*zXpGoM6Y!5G{F5WPwS4EVW~qo62|D+C@kEvzGX~4ihEfawu5uj9#I9<;Keuc`GBH`2 z+&|ZbZ*{{<@NjQ8({+Sw9&BL?r&NqKZzAunmZd2w zM+bi10Q>NRyP27!)$3GyEE^#llpbB?*m3cku~MD=Eb~FQGNYZU{vlO-{JX}^FfnYP zc8T0DdVt3^wU=EaJm(%VDQxQ5_DKa#V8rP6&W!O8eWe~B5fK`A%yn2N$o;Y)rS9@dCUb_ZjuAxPpBPglVVyCelj`JY2TPifYAhLn-Ljre!y9x+1aU?{ z9Y{C1rcCuWh%A-?EQDUodIkk46$$^2h#*;%2A(d> zdZ1@#q(Wj0P!aE(|F#iXtroiT1e->n$tqO&r)mz&8uJc+F>AD4b*(@Cg_2H;%xt|8 zE<$gwA%@8TKH~{3sz$ctOG}uojg+u^h7z$9d4Oeau5!pBG35H2FBfngE7YIyJ7#o) zIc`3#(_i>Od%+5l3(?8mhHI}~OR!JtF+ROg$QrbfkuG|akAO`~-p9>D1~G2ly$TQ4=uR?DNu19| z)==L$^G{xH{=ynNPX)m8@pi%^q}eI z3L%HNsh5NRc++L<3L#jG)eb?_UN%~zKA7LgbfDl4gFRPJblBg%4$E55!qjmwxsi2D zi5MlNUr_|xjTET^q5uw(8UZnLPN*^_Gd&&bg6$ggfee)W;+prkl2tr8gOlr$;oN4l9dDy}Q80Zc1bo60NZW4k+e zy$lEX;!6FeQ6EUKcM$N#Lu+JX<&K}k+C%O!XMIT?YbPu69ZYJNuN7L%+_g}pCeJ?J zL?}&*jkTv@()h8RF~Vp1sJOLSd{Ow&;y{ABc$DA;+3+74bFx<6v+?Mnnacj{IbE4B9;)|JYm)#Ca3r^5JsGWjm}>kBo%qS@k39$z14F_ zgB#{F0WaD&AD4<-#NN^&a0`lZEIOx*t=8|4CByTv+Hc!fXsvGa@{-XWY^8fGaRd;Y zJj}c3&Oec6&KMtP|RzXh5HjxCEaf z>ZNO(p{`(2FyJs{fJaL*o18Z;sx}inwl1PC6Q7&Ilg84$7guLrNpE?Ag;b6vCo+$b zpb`KoTP~iwgioWXe;9KxmIbWt{v2cOaO5Y4?98i6!Ez?Dlm8g#e^3St zT|F)BkN08_2-js?u=T4^)-y7?C--%#6)dgwhw$%`-@;E!_gu^F=FlWTXLaCr8AhrA z<|s+*?qCa4@Qq!DDfZaXRY5cwj=ztLQ7uRz0xJ^r-=X`o(+rn)j;Qp zlQ~EV_PEz!Q~vyzi%8$=bAvYQM;$^zz(^fj@^F;=t}=M4kRC_9rh;i3zmhKvG0lm< z-Ulx&!s4f0x;*X^DK#xXc-(=#gmIO(JUItXwiRndTho{GfrK#38Ov!2d*4C{4S`fC zqHkbP|II!S{YOk+cv2}nhWous?Gao)Ng7*`iJKR%?3j|0;zO5CsMYL+U=+#`uw%70 z;iTr~boipf|J`*(|2mmzc(__{dmr;1y4gwtjurANUsL?e2|d-ilrnw?j&^;&Wd7#q zU2(-Z*;8?hqS7E$01=;L*jM4i)d|Wf=CQx63XD6|1<<0gYRqk)Qqd-p9ohP|L?Xds z(Bm^<$eCvmK*|5!3`BLQcsVfwo-czv&dO&!dl=4#Cps>Ej%MBlRPttPrTlPS+49tq z4?=`JL915?W@s=K6(J`Ut8F4Npjxp0SPHY^KNjiC0dsKoaiMqz_}xT>*l!_QYQ(Zx zP^xVgLC=mJd-ZKIS(}{ksLgdeMt1G`*IjR#+NOprZ%py#Wz+c!)Jq+}zGdAnrC%^} z6N0Cq`=bi9Sf%y90!WYOO^W}xoGbQZf6Hu~Qb8VZFop_luFmU*@q{#sB{g4d{i;Fk zhtne}V6s2#J-lzN>4EujI9yh>gZV}`J!5K35hYKzyzM9z7W})K@gD&L;%MSYA3~vw zD!w0v2e#R@nD|MdSWSTxdoZ~Bl7f3$Gwp8h72JsLfx62gV$?gWQv#(@_ZBAJjj+gD zv+fl*(<8`MrP6@^Q`AlhT~q*POIlc@1bQKFnLQ2lM`dq9i710zOdVs9rV@I2Z*W{< z8L#{?|0JK6j}gZay0qe{vk`dwxEyS)(61N7BibBX5}qIS2Mt<=P4n@v!$=Qpjo=dE z1yk&5jetf5^P0~~>|qYVu@_oDmb zc7s84%ar{k9r+K0LoOjYHK#}Q^GtVt$O3){Ic8UR`V+q5)Ug^{k1w|pN@Hdz?JCR{uIL;yDE9S z{g072G`FS+bHSR@-ink!o~>4{vsjqRjZWX(fa3tw_0pgHj_HL=1LF*U=CswmJ7&Ly zFW_#%4A?U+$bf)Mvz`g+8|I?gr z1#ybMrSbcMASI`*EO4AI zOy5R>&^s5lBbl?KH)fTLkgNo6PTw-DYqN2B;OSxV4aYL zS0Sp^X_ww5Qt%+ok>J*ZD7$A6AsYV=%1;(pX!-dJ6yo)PpS&!Na0*`6$KhJV z5ik6^@!HUmf{^*)$;_LFDOFjzJZjR+|j+1E^aZ)E^H}}H+o6rq&95q|3XM~~; zIC(wIp6I=~vv?-Coc@dON^Z;p>*>NNlkYS*-`-gR;>j@(B0M=wfI#;#m5ekM zT010ZaWeu`4=DUdBQdD_@B!gl2x4b+%2$Kq4-Z6vEv%>RKj$(3x7P7r-7_CZ+6nGo z&o2aj#g>rwKdS&(^g$iqIMC89W{U)GnzV|7eHrwFyN>gQbUdP`npdh-gDH-8b@W4qy}fK ztG1Ty9^V!kUdZ%sqCIazg2s^jL#5)vMoJS>Ep7327F<4DL#jxo2B%E&mSVpA`Y89n zIC7Pq;PRd5jo6h|*HO+mn>Rq`x<;d0BF1Zhv?;6axo!)uMA-W-Gz`t`qjI2|3$+ zw8Y~3fXnrynqJ2a*(4$VO`UG|9f2NngY;uJ!bk9Z5c^SbJwom_8ZlZnVrxB0;t}c= zVQd8Q1RHMNLJTde)~q{;g*mp?rN;97nu}ZCQtruyYo^U&F`1+-OEs7o(14$gX<~%M z`0MW|EnG9N_k-d$F%EvOT}XtrZAI!%=6GTau?k66+DcIa2R>QbyY{itS^+A^}Pm z{W9uPjeDft#|*H*hiU9*Zu?79GtpIoIL)0hXe=1LXRX!QZQ{rgxL?5pbzo9HSm6>p zIWmZlSb}|jGi5liJ6}g5?};}>>8rbOk_flvjLR$W`K9Y{_5*gJ2b1Sk zk64J#ZcY$xE%rJtWE{<0!J8j9IFu$AWoUiFRCFgEflgdeG)-{p4;c+qThTJ*rC;J2 znwb#`N}P7QjP^J(02XLx5uXf~)7OqCfhGc&3xuqKr86IwgOP^RHkQN2V{L=JWxpI! z+5BcoELOkVPmd0UV9=v1KK%zhh=e!w;wDS!{&~^gxSW8xDEWU)F+rYVo$)i%{~_c(^ThCbcAI7LqOh`V9kvy zUze1KJq;5@B#Lqyd~R}vV6FC(qe-Bd?a7i3hdoCO%*_mV=7`(-SEEaAr@>UP$8}Jw zOqF0V+HT{LZNNdR_8$G|K#9JkEOI3zwY|8%ZeYNpmW)&xE?n;Bz6wJkfI`%)Xf^$2 z0}(0sFXBfu@GVFq!n`&;beiss2y8#pCJw851Q%zxbC~Y5_&W*(+xJ^_9kd^+hzgYK zU(`<(0EwL)MueRjX5#`qBfn&)Mw(VxNWqy9=pfqXT}JbBuU+1x4%Y}Q?GKrGQ3I%_ zof)gBsUH*SZMrv^ivcD?=WXfJ9w4?(Wp80PgX)M1BEx~SJr9O`vhT>@YF_xmHQ`mRpG3YXUvg(FfF~jE(k%Di6R3{ zYU6tC$4op$L>%mSTn6Lw!6-jm>|Hpe0ia~i(KB(cp{QBSQ(BdrwF?;D5F<*6{OHB zf+AVA$7Q=mlc(>^ah}mqInp^LTW_TEVvJ-0eQ@DOd|>*=e#u{`h!Lu^bGbE0B3V8$#?qF`?^%g2=_DF%;E z=cLG=fO=fr9LmMa;z!2j=~u;ZxE1pZL5cV)scJj)zPN``wjLKj2;XBB^oVWSGj`sM zA9Kz$3^MVn_;S`A^uEZn3_vpge+++PRW<~rXx7XjXnyaNW6-vOX82sZcl*;Iw3eNr z$=Zc5q}BUGk81$ETTyN0i-i>J4+$gHt4^Da@IE9T5*vm5Q^Z12?dqf~s6dJJ4n9<< zcZLS{1xh^6@CXCR@4pti#a~I_=Khrc_Vh3FeV=nZ%UhE{0Ql|z^_tgi1A-hKH2}L_ z?_br5MIV<=c$dQpt%_&s5k4_fCp8d~T5 zZ!YFM0e>bnKh0HX9~8%;H*b{PgzjI*Cv=5L8Zd9dhk#tDo2Ty?WOctb2i@}-4?FCT z@D&R-cj?rBuCaRw^z*=F#d??@aYy(aQJvz1c)qv6>Z*Le;ty(#f@&JOx-JfYB}?cR zk{jXlXA-@{svBXMf0Fm?q3?0z&vW#J@#Qd3#?Ct5!{jjc(|8(CLnuheTM_~Uf3W$FIn0Aev$dJuH#NUEwblnZIR9djfI2tTnFcc% z_-elR4(=oWBhF@KVt+8`AMJ<>wd+~FFrvF*8~srSsjFAMR)!pAOgVv;Qd{tc%nPma z$#VO#LnXfG;k8~HkO|cSd`{oOG4}6|QG`_w_yEIFHK(>7D4g8U5RPHBr?JwIl%!8* z`4hDbYwSrlRe5n;6$-qkp7ON;=zf~#ohwjS`*D+l6=7=I$PvDTXQ|I1)_it&`-r3C z#3=d0y&J%5Eb4{RIy2_8ilg3w4LE2dJtiz!DEi^i`}tp*?4|9^6Y0%=Ni2V!U*A@r zYJkp{Pu4pK5toAyV}_E_$B-a8;mGId)D^<}?pRmS7=+YkppYyK3Y3* z!xBc_W^4cjI47mtL2op|T>o|f(t!>vuBy;*wGs^J)p^6|tbwR5yruLYcuvn6oN(SW z80Fdy{!#;7bLvA&c=$i{^cb`37+^@=30xjbHnr@}HQWJ;;GO8-Xg=4P|Har@ z2GtF{>mDiYUfkW?tx&8;i@Up1T!R#c0>ul(9g4fVySo;bjXQf^&i~#sbI-T)A!{a? z%w#5+mArY@?|q&Pn4#NgS+OpQgbOrjYXAIxz|{A(4ty7H6j!^`a@gM1wt2ekOpyghB;6?v z=ERhsQ14c&%Z03ofe$ffX($ghg;9Z zS!XjCLq}kgp~FBsrK!N7T_Sz+L1{{OU*w;#^e1!P^$f&WjY`4xN`a{S^6DwU#q=0B zE2uEVcDgiIVtM!x8{UcRJ}fkHs?Lbv{l<5D9R+8vGJ^(F1$5+i{K*|yjxVILpN9jI zXSZk^=Xe#weN&gjRmscC74`>iJL)LgT{JJ!jtef#WBy(d zTb-W7AvLrz00`RC(opuTKusF2M+xMRomD=9Qx8N0*~Vh?Zki-n{E}a~5~M+De1e4g zfdxw`P%QC~#@|0*J?k1aQ6C^}nGj;fi0f1eZeaym$oB`s~O1ATz`fvxzUa9Jo43HN2m1mc2X)qQ;nlw;lg2l0DnJ0C&J zvp~EZdhJyH$Pll#WCX2?6eGo#WAWu@2m9u$uEnNz;IHo1$T$us>D2glEGtzzovL)z z4j@|?wno1O%l{$ypNbbWcTrLzML=waMxo(U5yLHuV=BQ{-hfF;36xQ8@yy%qFa1$( zp+;I)r%W#jPqa&4B@n+!aP&h(sk_)>wUoHX@*+{jNf9Nvx7S_Lrnj1#ZGd-h_*@C4 z*74%w^J3Uu=3z@~3`t4T3$c(#=V^@9NC3QS2NqxX*_Ds$EOpv2@3``;-{L#2yi_gw z{&e)=-GZ*wmDDHn6ju;1PelUk;RI`Sp$at@5il_0+469aiFObp*(H#1?F?{z9B@=y z`5x5>A4b4&OwLm1K~g1GFDRWVG|o2m$uQb(!|nTDBBP-mXsFvV&2HNfTzFfFfNe~D zw=a;lca{UzWRglIX?`TJsO&!pY4OCfY(B3!QsZ4E!!K5zU( z@E4f=(b6}_S%{ikhYYDl~p6zDKO+37@uw|S5Q5!y*O=p#T59i6`2}T=v|fdjAg08wol~8 z$cxL-sk-+>^Q9_y#V;6(_PM1cF0#1xJ&ap)Cw}*=#@@AsTFt1y+pRGu;EanLEgzK{ zBmigmgMxe@5EZ=fDpfZXHf1fw-wz&dq#b&|oB${KO7|zZ8(C1Biu+sv2}k-a0mB*@ zKt6^qIEdf~MFtk!SLJv^4nPEMhP&J4BxSB3fRtJUPVAo$n*0DZ^uk7}u%y#tbqAiI}n+wM+N^cS#$KGVTr?kkpGN z{N>ZVA@_6gcW|-RyWLIIKRrs0Uy!#gNO*kG(cs6w@|pMF1xJ8HR_{{zrztIQ5)>=( z@|?ci++`OtiO6&ol=|E7F6EkQ_!k7#j%S6xUB*9QG*sW6H7?+L2rYFJ&*sm#7r*Bh z??GNMc)*9i928<22_#bg?xf?K&|5}2*wu0}WxrM%q~o*CyVOs#R)!yO(71~%nPM*( zLY+)cLA*-D4FlAY>~h10NPM?REwJK}>qHe z4jQJx%G5G6fMKjs((4t*&mb?N6KAyHUo*$*@h>C98G*IZ=V8<+$bH$^ciQFSCw8)r zY(J8-`&CZgdQ{GK#x`x7Ue$^rvCyCV!LKJ?#B^En&gT!W7A*N_rY^GCp|)Z?yBqrl*;Bm$-Bg7*b#g1RKqrRsH~K4;D-T#bnP~RFeS5ct@B&4BiB&to^Nj485!@Y!3iBS3N7g9tLH8J^@*%^vW96Y z!O9fj!dG#ZV9;yHN$qK}r5#@@p{gBg)p9v2@H#YlRHRbr?$PIc_+63B)dK;Sn3h1W zEwo(yACaCX$%Z9ymCV+F1bV*xLjrT95o(l~pyv{B+~#X)I+8r$`0VCNyOmeF)3tZL z&9gR>90lnd+%IgmnR|d=G7!v*vN8-+^ceSK(~jBS(DU{L^(7>FE?!@(C$`D0x3wgA zvbQ(1*ZlI#FYw)+RNAs#NADc=Z5($@mwgmXbqVbIRLW>0*G%`I-ojfAF&3Dy4H{CiAE1q$AG#9NE^@^n_inZ=|kH~!>mG*<(T-1 zzfTpy2bCS#o1KbH)uSTp@_DDmiB)s{Zqj)BGx)3D5Q$P9yvAF^ zQF*7VFPhI@Mf@SBUEUyGxwBTi9EMFcUZYwu5YyA-q7i#vnr=)D`g?LvX(bA?`>~ zN1A_%x7!4td8#jaXs`1tIwQ&uNb$2t96_OrwCv*oF7Zw{P~lhUh|Dx5-tGcaa*5GZ zTB63zd~HUozkb~K{YZuAbf1iEGu1qof@!@1($2IH=lq!?;9@K8YNI z5iqutvadw!JJ|N0*zuYA8^i%0*{a_L2T-v-4WzBbL55qwLm2kL9QMB26TJ6N+2scf z=dAt?zGJN(uu~&Cx^0pw_Z8)$Zg=pj6Iq{GA1cbv*2H&q#rK&bNvM?bS6Bt~6^pR0 zwgIw+NxL7zIcJnZtLB_tB1yjH2)`{OWvHyS7YIrkkMwze~ijzNA z$K4YW|ByF-LR6FLOg(^$jTRu!-#&Hv71TVJ%7~}9M_!jhVeKG-Nk}LT*62r*tQ;gF zr4%B2MegC5{8DN?VgJ3vGq9VbAyE8qUwqi`at%B8OP*3^yf}eng@xXexKU|rZ*Lx; zn(ZHZgO^p}(&+8gb|&tGe8X!lf~ijQ`v^(^_E{x(xaK9-;s($4$SKV^%J|{;eJF9UlW(ha99k z$Z^T0gy3&=2ag{f#Ph$^DczZgJN>W~Q7t$dV+&|(ELR-S^Z)@{+g}Vn$$TR|rZq^uZeB<8t&VWUImf?q%!26oOLJ zZan(G7<^gOvKF>19?UFUhZatwIV zu443Pd7qn+GTX7MiFmVar5%)J2(rVLxr2g>CEo0(v+)4sg{KLtj;pA~ zg1_rAY)@5(dsmmwhj;ECcdrL~M6lO7khi0^wWdFDryP&G;531|+HBXbJ8tu}RX-n* zkA{XGVwEGBAD^3%U5&)sj{Q$Y8z+afQf-@AG7hWi@7>jP#vOOQ$bqMa)B{eRA{QVGiV@r3tTvdbt5>L$qdJ@v zghWCY!O{kT@-8q({9buD;W~|fM_7(-igKdr8rx9t!lA9nRjJjA+&*D`KjwfarJ%W zw8hhj57u}F6)7%i?b$a~U zKZCX1O|roJ`ElfBh2;7eJ;SR}tXk%bIwMN!^_RPc7~mdm;DP#5+`%S<3K>8}v3sj? zrxdt?hjg9jS~z(2oi#6=zn7k@&AK{k`sLaLFGl~&GVB)Bl+oZ^v>Y6UiiA-!mFo44 z*0k4ns*py`=;^|H(1`8s8XWAD2VNtK`4>ITi-+vP1Af?L*a_dKx#z*?Tbc^L#PuA)>MHy(N^X^wXcO(DD5-Td4&tNA z-73F^ApMQ&C9t&^TON2XgXL9wHTo?I6Bj$bgVx;H1ThHys9)I$%vmVMH@FYr60R-m zXI^rf3IDoS=hz@V628ogxgpA&7v*iVZRza^@Re8*t9vIHR<&d|+OX6>Tb_f5OOtiG zz#HM+=!fuR{cM;a4OGT}!rLym(g2Iwt_cOSQvh!m3H5nv?+EyD&rh&1wfK7SOW~Uj zdG3u;83!bOcmJiJ$f+Fvpxr84w|fsANN&64DaoP-HU;tY8l=Goq&x$ev@RMOFw z!T!taK~#*EY*OMzn!TRm?d!uj$LwQiv*kO2n3FD5t+N@EJ4eH^4P>%xmAbKuZF2q< zNN7nY@o!D>Ol|&GwQJZ<13FJtas1gPJBubfA1?X}o z0Ii#N)#l;SlftntnL(VBkbwG0kdm$M3{lpyWs%3Bwa0+ahIna5#Vuzx{^8P=b%)Z+ zVQ}UBEm+*(g`atJ70T)9PEmOh@OyJ|&A*t3Y*aeHy`~P$Ka@qsDm&WS@rTiI(imZWH9!qj@ zn(`1iKgGm_*yfq&$HN;Moep`T*o+_kkxq#Vf0}2Of3%fdodg;@A#2G1yN&FxUwjxl z?F-MwTv!c69eF167BDIjHSXQoTeqw;+im!E$Gq-JW(0P(e6jR*l@{va>=k!sEc0h@ z<7!nLzzl6jWJ(skFRyC0eyX1Qnq4yOd+GtH=iOTOr9HmwaT;-@CpBYq;SXwfZ|yd` zyRhreZ8Cf6e*3^?J@FOr^(XRkkjmMh)2@H*)#%z(p3Hjq#ZRrSYRZ6wCerH>uf9d9 zfOuHTE)q)+b=T%+4#h|JME}j0&C6_6PF?V=|0u$vW5a@VegE*2v|DX%*I27C&mh5= zcjSO82Wxx7hO`@cS1-5UcJox{N^KVFqwA;@3fuaTOZQrzg)1^pYsg&6$$Jx=C@QYIY`mGLqoeJAfnd6W8B%x%9s^^b@l0?=trVYnj2` zKe{X*b{U?nS;et$FIn$Tb&GaMy&l31Lp{BAsa7p=LaMaNDZy5ekB~De33}NNkH}Qr z7Q1`TjC;>Ug86_-;0%4xj7slOoYz$5*v9)jUjbFYEgA7!2Hh+PV6h-s;=#kSw@qX< zNA;hsWD=7Jsc3?hQ-!b21Ah8FO-`lW(PzYB+*zsJ2NrEs21Gy3_r%)DX7+@Fet*`x z&GxmFL6u8c;p*xh={O&a`p3-}_;lN)yS3?Y+x0wXnez#vq5%0@m@R=ez9zMI`r^mt zOv)?JArNf_b{J<6D`pqQ+PY|aZ!>3Jeo^cNCOYje*+ER#9DU>hQBl=@pe=+r6VKmB zfSrw#8WPuq2n?wh241<~!dtf(y1R``&UNyLuv|}T?Q-Iidt5sWT_s63`K>%1T-EmN zSnVN<(yP^HlegsO#NT~+PBUmnHrGFw9C#)!T-BMCVIJ1W=U_+dc*|>a?XQ~t4rm)J z%-JL19zoOz)%`MWSFO_;`Hw+jFhWcCRX>Nc&WcQIwgVS5B_q7n&2Po%-|UP1ti8L*K?esg8ql}6PYGfJBuPH>JKvkr}>1zrY+ zP1<+|h=($tkM%NEpcU$5s;C0s{%#~}{L?8I&;z0WsM5#X*dv87$q}~B2#?HP{iw37 zw{De1(!B`sbFp(^_nSNagT@Bcun~#lTJa#SCWhgv&kYB>6&r6pMUk%(h1g>^P_y?- z1vqcgnLgi=^rAF9mE5Th#Cjfb07_lVzn@Cw#pstxe8~wtLHxU6;IdFzH5Rv92;6Ys z=euk^gEzoVco~wa0!E=S#)J+*{3zW`0RxW}j|u%(elvE4YOCM>V9d|bh0K1285o-Qvz&(e0JZ~uj5t~ zQ7@A4SP@T6HJCRbiE%3)+nj#C$+@mtcB{)!mjjmeaPDm-kA0%b7{aaCmb*Z}WL}LrW=prouUQN5O-egp^w-!*?vD;M81n$k;E0`^QrOJymhCUUbKqR zzFa9&5&Tkp@``&~zZ1{uMat+iMdoA}CYj;`TS0S4KtrnRg|C3FhKYA9kqrrwt^46f zhpL-F>qGU&w^!-FMII_Bj+bv-o|fW2=?==_T_r`B>)h18pWTjTgd;hU^|U zt23)gNtr+u!MRSJHP@c=7qx?jLYl|~K?WubnTIl~ zu=FYNJT7)KB-Yb!_caMG)yUjM@I{uEqMVjtMh6pMI%$qj_HTn8Gbc8nw9cGRBp$l4 znfPbFhy$&=$>EU}w%1Un-9a$By_A^r$cbkE=JiV|Ck!+`bX=+&47B)XdXiC-nfJHS zit|3irv&nNdn>L7u2l%Mf+X$0h@^vfouOqKofNsrAlIRQU}w^6(3;)yw+-?y;7es9 z>E974E=;bDspBxvlmHw0rS(_OOT?)AoRafM(%F&-EnZGk!SJIIh>zaS zZ4?;y-s!5M#*EUK|J4kgxljtHKS)Mgk#bcU%=)xe(G&hdS^|(@Rb?+k$lD8vZcB#<=v8SMycX-S4g+6kA)--;Y4BK= zD-nV1Beo>codCr(Tmq=(bLTww=%>P@~~_ zEpE4<8v*fA09U01z)XVdGe2cbk;Le9YYHW0zC}14n1KIDPPLLhJc`B*Us}F}swpr)WQl z|IqOn!i>cc7z^rVw4Q;eR3Hzn`1Du5bfF@&D-7pPLy(K_(!g4uz*}=OfE_}T1@z3f(K){0Wcpl^%p}e2(1sFE9v(w!hmMz~)KG=fR7d}K8jAzVT)XgSMvk!@yZIQg zC%rzM`b}D<7;zn>873VyLxO%@;28~J#&z;2k7EU=OL^Q$Y1t#IrC4ft>lGNJ2WHHy_q!ZxmbaQ`4#NI8jWgFI^8BR0di<4p`EdUcX%*J^MpWYI1%8ksWzy8Y#guIC zT<-m=FkZ23cVCFm0R7SJ2=AU2WBWYwQ|%{}LSkVp#4w-h9S_9>Lur8C%oxgzSdX!y;_SOhqiTp?~?e z?&CDdgdLwBUEPjbDXrj)AJ^t_u_D4F)bIzN3#yTn`@c<}q_X~As?uQkacUO#73Z+x z%O13n{4_n35WYU^VNVx%5vrXt?6i0((fM)%C*CCzm6hXI!@4QZhT#N6=d;Y38g(u@ z+Q;c-&l`0NAzOtQfBMHuM=cP)oCI2OdB1sXou!m2_iEn>CC#lUgtyKR1+sONC&QYSU-NI^-jr z_2!IE8Vt(5GF0cQ4w21t2!$R^7CY#1TK{6D-kI;q02QZhz4yuVvc3RBPg<(HZdsiI zPoktlaRbGl_m!%a_2WX8MG}_RF*!Y-hDaRVDr<=K;!|dDEdSvz2d&TfD3WPPsn#?a z>iO}~qW_8dMXIHS@gW_$Q^6Jnzf`on`}glt6kJ+5MXN^)EK7!ABoRvlQQPmWMjTT< z8J@I#WncS2QZ)*y(W&VGV$b(P>0w1C7Y#(ZP*uvz0rMlOta#K&s%9F(-E$4IIwH10 z5-Owz-l^j+&dk07TIig1|17eu%idD*MHPL|GUS3Jw`xl=I88dX*z47tYKQn}DeI@o zDQaK1bt{+^$BYdt(5AU`ktvpDjf5!Fsh>RvRt|Q2-8#&geQ!~L75m_%cJ+`x+(Rqf zH-9caQ9~(n6t6lBnbZCZE4uT#lpOa@t)g;WX=3ifFMDXzPIhQCbSL3z!ZEu!LXLV6 zN#(Tpm5&cTbX%6$*fg3Ub6mCMspM-4Aa@$d?CIlqX`Ikx;%5|H6;c%^R_$=bxrsHw zLnO8l4|rZ$zd#M3p?}ck?6Y!Gv`4}xUm<4$-00$zKiA&5zigk&9I)aQ;IN5M$i$1!p4De8T$vX~SlB_%EIu=W+sO)AkL$YRk65%npVz^a_opkt`;pN` z_@c+p-AMH;#zNr-$KgamEwVErgP0SfzP*L~9Ls{LSYtRQkyJvW;)A>-jC~Z};dmv} zr_VlM-JTuE0Osj3{CyA+K12NEvo?4Ue{6#s=;^D7dpiXR)vDjdF_fw$?OvCvDS=og zzGotSh;XA~jpkd=7ydTq^|#3CjaWzxJ|{%F)eE4|kJ4JfU_JYPY*Za9N{DBiecB(- zrFjh7DC&bayOv|X(#aW!A7PO*TE7f)CugfJkv)W`@F3xmSv~#5ewVYA zrc`!cKT@IT6!xAB&33K+QZCiv(i5NXo0BtNkn964OhC%uDHzjkl-(|7JvOjsAbj{qH0<^Hn(1o#4+Hp>GsV$?MQ$yxh(`($f#_J%lrTbaW$%NuL^$FRCNwEk zPB9<0*5?nmmZ^*A(B!G_In;wMzfuOa*5wfuAT_7ULQr0j_Q5jSvG zM{g-UW1x)0r6CGfG|wzb7PHll zyx(@voRe2*_Kz|+o{2I((8uA4urlu<6H|K90}H9L3CP{pBRX5Esc4wcAK@KMZD!jQ zQkgNKNt9)&x8x`>B@HYKi@m(Joknll2H2dA$9UbIgisRgr}U%#-PQ+Y%$YiJeMIvmnYJFKF0yu`C3j=jdxi`V&- zz=lFW#*1UB1r{_FKz=Dd?Mk-hW;x`|sZ28L&mW_ro=GCE5raGC#M;Rr_=2ng|q`>qPSQjgU; zq8L?u980bnH^^fGq7Wy>gEq|Qu*QjJv??l@p1SXC?kK=ZroH5E>5EPErhR= zQIPsB9YCh zMa(KM=C=$V++BZbk`rHi&A}1X{wLC==n%dZWWeOU<(#Spw#Ri&0Px^`)>HPPg<=c` z=_cmY{wz6|#vOe`Js~Z5(UaHOPeEu*7NfX%G;?TbkXYh@(s{Qr-t&l1Y$%?l=z`<6`WhXcvYERpJu*Zft1wMUbSfs2PCH`c-qNHY9RyN1#2Bz&!hbGj_uOM|=D(DRYO~S$MuhN? zGgJwE#oiv=bYLIwEl{&}HQ+w%;pL`u_4=D0f(?2k6i@#l7{hpQsY_hV7WMG`3&;ZG zlYcgQmZ?0d&O-wholyUpYG@7vJsj5@EAutMLe z{0^DZv8Z7%`962d*+AlxwKyqCN6&$5Tl7*ujxPp*P+ajsfKa+S$C^2%2b*(l&YO97 zzV=HC#`wX@bU?{n*cReBy-udQ5(GH#Spw~W=Ee_5Lr!eB_^ullRt;6r zg)ebCA1>*XQuT-FFU%h<*xD~&cF}Dj;ht)D@xu`6;Moun zxrKR^9I=YKQP(KxJz_gF;p?p4-h!OdEU~xWC;qb@x}Q1EpulxFpP4eR#oH}ao)GJAeVRa< zhM#GWa&WSqhOrqJFE+7mG(A>cF4cFp3kos3n4V-;_RlrTY@88ple_1YEt6_7H@XB| zx<>-1f-hJjfscxr*k(DSWWcyX1;O$BNo)@XxqKJlKI**y^Xx9V$I(jud_ejZo7@+W zCc)ped{H*zI`lDerhePcGWTkzuC@NQ1^ObhVx&cCIDx@Z4N%A&$|wQ^zcx4UnZ~+X z;-ePBa7Wn;gxIWZ4wS>3?3=i`*r+L-a`)P&1kHuUSMUnTzNK?|)dBj>Y!1~XaKwu@ zXbb@&#EZ8PuqwuV-}6a-k|Q+j3?D)si00kcD?;n3hCGUg=r(K5UO)1ycI2Y0^aSEx zV^?DOTVBfPkPY2djom$;E#ORL^p#=UmWi)=%<_Eh!e(qqt^Y~1IdyxCj&!G4z$SF1 zw;{HpXOpME_%|`F3Irq`N_5MO`2Jcdp;c)gsO4$8V5#Et>lOXH(3jC8sOsV1r}D99k%+;g_veN#13Cmp~hiMV4~(7GEN0Gn%{j zm?_yzI$4q0z%nVm#CP_75L=Y6mR3V-)9P(KzEXYAM~<8@twg(>zy2u<4@+Vl^d^TyAMq@GUMc80HidmDhNYEepTI1-HoVA`Pgl4GP^Xjtf) zIUmA%RwmVdAZ(y7#J|3cy1wmuhsUzUlAfSss^FzbRS;WF4VsRCl(WCZujM5BiBQ3mU1S$@}PA3}K9ZP4W|E z!%Cj;i*i80|FD#`*b$!*bKx*oLJ>>j2{&O(5++~=yINov@ei|H_mm7wQD&Me(&94O z?(&b_1JNoNC^V?;c2{?D`6LB2cv69k`6V;?ta`_M$#QjP6JuY(^D7p(1pr zuCE5_2)Qd?t|>|Kilgas3~H@6G&lQfWjK7?Ge}ghB%4~r@?NBM<~%-xO*)m(hGujX z@{3;`yNsC3qq-X=gZ6B zQvmuLPRh}7s$3m_@9E=FEaUG+o4xj!t`iq>_R?#y0Tp?rS+{b;s9Tza z!&aSZD)Nwogbha?9Y!NhLY1cg5eBm|ObU{4J z8S$X27VL|8gI4`4aK4!_lHpOoK7-Y@G7U4D&W*D{Kgy8c{U?Fy1-_n);wpLw?tA#b7VOANl$_CB>on z@8v`i^YxE>Uj=~^o1GFPMNeHH;pDIX-hKj366+#RrIT|{>O}C$A?+abG(E#R6TI8z z?vp+a#W>e0G54}*1mBl!TZS*#06`QD0wX)FytHAMB7yd$DWemvIA_Wfbtre(=v40Y zjhaS4(NC(^1J(Q0o5+J#vS^jWpyH&@82ctku$S#}_@t80G$~^5K|B%M-khme?9jx( zmgK*gddrH}?yT>RzJ2DMpP++H9?av;Wa5TizPx+3aeQf6Kh0G?6AfY!ZFt?e&V1>< zf*pcP3V_u$GikRUY3cAj`r>|kd6-;&gF=1^`rD{S^E)1h)`63dAewiC!RrJ`pE69X z^4)SsUldk-z9__smDVZ|SIHjH43sATnvYQqi@xVHo*xxjPa6hi{5rAWEh)zNUc*gl z{VmZS<93IAXwZ?7&!Y&Il`Wdz`-{^xwwx?&!sg!7k0fRIJ8s`c>+WZyOeSd?_=@cA z-V`BCXZh{`52}H38P1X0+v)AI+%0D%s~9@5pzm56eeLR?$f`>+DRoMVwup>HKn)m? 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 YT5#mUyWHJILs1!VY!`OO)DSE1G$O- zH11dxjgm-fR_L%OiqO)bDG4?;*o2c;=}Iv%f8}se-|R4kc(iASHJk_tIHNELSUY3{ z#+?!U&Vj10P4IeIY>7vdChqMfemlDTVfNc)={D;23URxPztXpVjj|9_s5#e!s>idw zvkVE*x8FoowlM_g5Z&<4cjj}a7Ju};6@Q7Z=3}A^i}f5ec}{m%x+3!nl?;5$YZnio0KW|$lLst zKu8gp5h~3A9`9zoL28|V6QIW{HXr0xnB(0v$dhNUzByzos#CyDM1_NFK1x$S7|?uXNbmx)fNSo64A#L1e>5@& zBnW6WqCP!Ey2{}CqQ?~PN&V4EnYdE8I{HM~d7H*y%^li?S#Tilo)%ipeeCOe@8J%xbbr)X8^oL;-4u5tfJ9P=n|}y zX@c@cQgw2uYT#7XPDjflXnNZ`r`tGe0dr9n%w_PB?r<`jX)?G4oViqLV z$-rWfstI{cj_t%$(^{h>LG+s^%wavjI^z|u$cK_UM`j>Gi77U@nMjrOPBGFv3=64S zQ8D&SJ0%eQ)|G9aHd+-uf9vv9At=Mj{p-q1cMI$1inwCKR@;?1{rRjI9NoKf>Iu!c0h)B57zSX)wy!n7LmM*OO#_uU zNyvUo`7DlfMhz+nipUu%Vycy0-t~R~-Qz-uO8M4=Px1u~%w;2JCD})eCk4DRGSbnZ zI}4a@5M|@+z-CJfe-JVBYJ_HvVaLioVZZW)fucrkMygP~T8n*KHdE#qqHH`xv*Dfh zjTwA~Z$IrjdtUc`55F!2RfzU+HrkeWJWKM&L0rq+Z7E+M{audf5O-#9$4NIW<@m92(q!I z#pVpq&d<~olxNLr!4dFNZCGdo5@FDC{o{sL39cpvE(VGkwSO}~4awV)HL`BG)|@CE3PoCb zEf!RJ6xGxhh&XHzF83XQgk8Tun%D!aiRCPPyLjo_lP^7@mDbs;#uF~-v1t_0a{u4D znV$3E!91IrnXhtNSw^S{(3bq6Zvuov%Pb0>J7~lIf5rD z+6EWzl;w^Kq3^}V7WjQaUU+f*-vVMl^5Z|Mm0FdE{|@*s%M$-FeFz9MYMk+UT5y7^ zBI={J#{(%Y_H=Lvmlt@{2LuFir>YexLI;P$Dg_Lv2hiXv?atSToLrj}`BU0MJP;n@ z;o?2Se_Yd5j#lAbs7E-MG2?X-c@sg#H7)sFCQ_+5KM{#?Ojwgp3Yo;$;B@#G!lT%8 zoxrx$LE`MqVQg-KS)U%8GIsY|Z>KFrNMye`jLkJW4wrAMB3+zUZahf=(GcAos_w9dQZzWG ze;eewp7fa(HQ1Hh?8=SiK#S9Y#VKmBSQ^Og=bagGCb`g&Qiar!M#wQAaYZ@lUBp3lkosOf3Tt#ZI*R`vQ|zE;gKS1 zx%mmwU&Z>j^i;p%cb`RQS13KHtZQ#zxb~=GU->rVnO%FldjZtm5J2BvB_}@+&nkkE zPBB&@R?paS-lWA?iSH~Tj=xL)z)-v>Bz8od!Luqqe~q$5I3a3i6|du^sr9-1E-mda z7cR@x99GuJFH^Y-d4msnSHC4Vf8~L}q>LgdWmywsFtl}sW`X|@8I&lQAlG+@GanH> zip|=5yJmrm7!YKLM!E^M=$N)-}#s8^Swu1lXsr^4{^`}k(77dhf0|B@msA$bba}>RKbJR#7clJQA<6}kk zETK|6cdQy~ZtZClTxHkLc}Em)8lnQZ9kM!$>qx1^SbqANrQoF~T0AOf6O{=Z#IJNeTe(@@pNTqFe$_e?w21@<{!P-&dyixbIuT_!GG`=60#7O-Dq@sc}xT2pt36 zr*{t$yz>zsvgl!KPNbGeN;Gc$8?)w)j%s>9>deh-dnA)ZQ(R&gDZG@pPZh(>5=T;F zE6A|#mL*UWjgNx=bIuFeS|whVZ&MS;&$p>X$tX%J-_L4`sG`tt3>ViQ4v^JA8kfXc0V5PgDD{>oPdkpWgE1ev9{Hs#hWrqh`&t1j0Z*4FTLE<)7V{(m z-wNJN#R}#y^%I@{dEa7f%kw=F1x1bjlb5+$0Wtwxm+V^sN`G~C*Zu)Hi60aahAhl0 zaZM2`AtFs>k!(9y)rbrb7L|f>ReD3|zVuX}_)mn`V%dZguS9XN`8E&TDvDsSi9~|( zd(j+3_R0QL*XMaT!3uQ!pQD@c>ELQk@ZSac|Hjs~lmEE|HGzZv|3vz~UjDMKe`n{E zQTQPu>8Nq^2T1$+9+EBFD%XGO;SN#jKoxRSm(Hc@pg<6677e>go zvb2hZOu5|2GSWGyKI>F)YomM_qb=`Gh(M;J%kP){Tmcw=?=SxEeP?vd_QfOA|JbP2 zw_N^T5A}ba@cir5pFH$WJ)3{}nSKuj|6@>a5^mC0m46v@e~W*-W%Mm%a-@$<^*0b;Ou}ub$+ngW4c8TKqW3xnL=cihiIt+QmL*k0w2GLa;tPvCs1EV z6kONdR!K;I&$TKn)N(xKIhhTp4fw4iBk1>yg2Aqh=XT#Plu$M*w?gZsGZYBk&~0}K zBM%B>K#FT-wn>fAAa$;hsdHizUGK3Cvh#T z0#XH}(QjS@RzsILXk^)Fx)Xu!Oqp!?nE$|DCLQ`f74jmAg~{}d@2r_as5e0w5|y#` zD#F%xq8DymbV(K3L=w$#|H!|9Bp#1LF(k5zB}>4(5O zZkE}q=X=PqOVY5Wj(0i~0gzeJem-WcYf65g^G2=1@TmaBmGaDq95tKkq7ohOzOlg$ z^o=G;O@2RytDgd?Rg-xUJ8ak5{U(NLX1@l19nyiTwUE2coqS^54jQ;8_H5|*cDyZE z&#gPb6<)vuLRuqyQqrkVpseP^f6q+^%*X$$Y;HU8U$xq1!2eqo{cma&@T9SN7iW6k z7yXgRub>z*1U_b{Ih4O{$zS=sN%m2wi0tzS>xf*VdktTh#B`%@2Gi;yw8x#qz$e6i zHC39DrmN^LFAPls+~t_CX>zX`8pcNA?iNbiwVJEC%^~=jriKrq;*rYeCe6(4jpEe| zHlUoBXR-Z0O?MEdICJC{OI)iP<6Ttd0uZA?XsIw>|}v*#x43LQ$z`c4Ydnr30xfo!V9)3dXvnp#?L` z(FeQ?r*{3Md{7Yh!|mm-L1C_%QS$n5>EUy1*0ZG0-eDG-RWn>GHN%ehn%;+h875{p zh_<9?YtZ2nv+&hG1m#KEbCO#cj~X|kXb6*rfSdy%z_C&3J#++8NmExrRU*YrWUEiN zu@P0LSIe~$R?%Xf+B5LZo08TxA)a7_2xzsP8O^*VfectK%D+*RiXnr>Aw%YOT$Zea zJ%jo~l&X1i7iHjyWMjS%KN9pqbSna$k>40)gQ6lD^`T@wN6vDo+soRYG=NfqI2WWB zeMC4$2N~AT-=DbF`3# z%+NPead;5nwKo-~POJ3L##tmOVT}2@PvDbG;b~gbT$ciur81x%zeTNDqZXHbVgWn> zHkZ3%0Xcs=LIHz&*97f(L`>0cD|^-44c;B*+(bu+q`SXhlbcq8kaLw#u27s{Pj207 zUG3o-#0=4>Q&%M2JusRkIT_5}x-);4HvAgY#{k^kG)274b za_%u-r2<)MZk~J0rP#;$9s>ifrS!()s>rv*i}R5dlM?p#8lkLQ7A&q&K47W^ZRrJV zW((S!^MW?L3+gq$nkf_+SVt+im9OmY*VK#KRJ;sM`DYG`@bZ?yOie&REsI_OhsrgJ zsa$_J2o62scn{uXIN!6q!pD3DJ{!G%i&TmGb34a$N*(DBip@!>cZi&x+Lm@ zTgN^%Qd@L^5on*oAP)BgaszTMq>imPwj;Ptus;ULmg29jV{yK3)B~m9uZ+sUNFcSm zI?gg9PtRdiB*^9lX~E{v?`rs>kdwcuwHtpg4wnw~)|^!ePA;Q-NDBb8sMhS9EgjK< z^}1ljf4=$u++_Q)`{{c9{bgtTc`&+KaQrt*|K~P;9RFRd1pEKf#eeIipCAC--8p3} z|4{Qi0b_px#{L0tJ|^mKai0Lc^6nN48rKl81TnWQAm`kp_<`}wuk6OJiL}p%EF6E4 zn8x`>lY}2bXvZ$ZQ9mN@DTes0#)g<7T(lB%K>6qYZaigGNIfEXKwbr>^P~W`8C`WX zzo7%b=9j));A-;?hx!eFFL*-pkx#c$PIH7KMMWPPx~!SPv$G^D^0Yk%&u)uq_d(!``Ne3Vr>= zxQ+xgZ(g%3?MH2+n<(S^dT7W-#vV^=vH#_BLvyUUgu*m|D!5yC@X6{n`?&4miK?2X zsye-1Y`dOWWTeoRT{B}3QI!r$EKOTy>SZ&Y$daA#$8ow3ac6q<1jpym>TPsUP67ec6-$|6qUe6$ll!-&>bc zLe;|0A_*U(*%Xu=*K#`|&ED})*`FW-(J$f(UH6eI+gK zKSD|n1ON^=RU;A)8cBaF`GTdn7`FON32b^xnXP8&tu1bGF0Exb`!21}U>{2IPqAXa z9$Ak_%QBMe@^zVVN*tmdqZA*1B9ShCbrH6BT;>3KlcqF;x1rwX5jM4q2L-z1AlMc@ zl8Do{;drqrR<-B!xFJgwEfR1BdXc!n-2O5Dv~+(#u|VoL3SP0^%-}J{ zHWfLQz9FizjdvHgYjf{}Ou)bx%LiY&0~(K%z%~H9lvz4t3kV8mNd?p*fui{PiEzBN zHTpZ!)8vjdQ6SRNYO02J2j%S8_6qT#9hBEv-Tg3{UTd{t3b!M&qLjtkJ|K}IwNJ~H z#u$`U&W+U8Forh`K*b$ zpMe>j-RVrfi8&EGydD)K&qk(sY&OMycmRt1;9xntoR0tRX5716AC7wcF;C!l1pR-b zTB{TKf53lw!u%h-{1f>9?445t{=~QrJDPy_w3ml-aio7FfA9e%xZDUj*N6;d0HPu8 zpv?JSz}YW#{vH26EB_ht|CZ+b|6lgrysM2Q*&Dup`%~!jJc@PBLh zf4cp%h4+7>!O4hW`t457&pV^*Tabh7^S`uI@$moG$`SwnG4=o6(%&Qx2(^hOSw2ZttP34fvjlQ&K@lpsOIZ2s-Ot%CRTYI9||AIJGsHg zN4G5^qqyZ0nT4#~zyZWJ9Hy4kk~aBI_D z!s97_c2=bKuT*>^Q@qjnm_0T7Ta*`q`c~uNX>A76k8oWGl!T33LwE9d(05)YavhuO zXh@e5`FB(66}}f+s^u;|f`Z<0JBWBKSl!etK~cF+FF{pL-TIYAa#O51I2pa=W&wWD zW|n^lHxFNA*N>e4>+WQLT>kIn|5{tCEJyzT^!R@i0ip;HMSv&*L=oU=-~T_z9}lAc zFRv~7`v0})|Mz(O-&+2e69DUPvvh#*WPkng|J~7m-1A@YNYhM-QQ(D`anWxj!g}$@o=3NxK(r?)7wxF-s_vK4a5uQ83v^kI2y+m`bE%Ft>21tIcC0bAT-xR`5e@ByG;06C3ufhJC*quHztk zu9BUioF@(CB*|+yORU9n%Cw2;={0{e6wHP-Q6KFZZTI6Q_%YTPAO&(ZL%c`Gt*>BXe;EaB@C1!k&wMF7B0t zM1`r*%0mSnZ**%=n(IUk$!&!cWQ3cj+W<=pVDe`nFI#I5B5z7lqXi;ExG;ai`Zfx+ zfI3OGV5kM%RzpD+nWZrP=*Xk^7V~DO4yJ&d%y4y9g_s%BF>6+u)LAeL4@{nn-2P5Qa9-|Pkpdi1h4CRkV8Ur zXd>GN3q}sDup_ZBQj@WOgin8~p;_)`pP4)~M)JslvN)&Lc2W#XV~n!7NUz;YZyCKd zE0_AI+Wh4=r<-H|3UQqxR9xUm3^Iu)Lu zSC&E=9mr z>fs)k#+=HV0T51Za>Ztm0(y6t(jJ zJR3)(@V$z{!Z3sh74C!Xz7&0A?I~#=VSf2_RbfurR&HA=+RUG)0N$fnP{*I4L13B& zNM;DGD70YZT24nip64&uG0CgLP7WKWcfpzeO3#N|(-X0U03KD0Dk)@VxE8+aY2vtBGycAg^C0 zi$P0+4bzm~DS4Hb2XO&85f*B9b&qHlxJR@p0@cnTP`j5=aRC|y?PmDP*Ozc{0TzG9 z(d-y+?x1ymiotgOl>EGivnN7+4_Q!V@-to0@Z`)Q`jx4J?p3+sN{OL>bXzxV#ORrY zx~tkl33a@~zlOz8Gr4)f9~X&>3T`~;;<8TrNf)hy=N%mn#w8-M-&OFNWKc9QaH{Pjj%oifiGtl zaS7QbY4OfLG(+A@q&)GY>fan}Qbbe&tsGw0528A0Y95F)5Lf*gPBRh~(} zQmI&VY`QrvOrcXaD3wqi39M>A#2ngvw!ylv5u)8+zAf4v5V3p? z^{sLouIh@cDT_6Ii`Hm#bG^mD`gc7r-XT7)%AK($>bekQD=WpVy!;G*p21pO+)|7! z@6>%NkLxrhbT0@m`Y;K*Dqep_I12J>=tmW;N?51J{`)z2b6IIb?iab=opC>uFTpmq z7jZW?MAZvLb$jsyxa`+yB?nxMEu;y$b1QiufBR{1Jzvo9yi&Z5;7KkQuOoOMD^IT< zRFEJJX1NRH^-VOmg)ehUH4eQLbS10bD^YWxd)l5NJ(~{tB)C`m#&v&1-!=`*R(ud@ zX^use2X-&CBS5uTPJ(HDy;ARPFDKmsY$lz zQV@M9kxqW}j6Z>}DZ@3*X8CtuDtxL>o3v-NZ)tg+&z+zt=PIz*T@qPXFjg^AZJcC; zOa8f1t=z=$%x3RoevZsC-yWT_3EAIovw3e3GNo0iA76yPZ~FvkSWRi>p8*RnFY}3W z{;!4UfEh&>5>jkfl-<0MzpNwo7R4th-Gg%jFA4aNI%r6>Q)&toxG1%q@Ah>exTFi& za=H4t6XeX7Iait;*f4AMFZXM*R!Qqb5CX)YPl^8J#fBCkqx&Eb;QnIO{K5+tjE}qf zv-hL2mJ*79$n#Q7ZCXF+gIf_W~tMT_!Zna`Cj7KTe* z^|0e=ohq3}9%~-0(-(5@imc}BZRcfz$ zH`*W6zgfO=Q8a3Ze~F!qgP!uul*#Qp_f4CT-r#8d5Fdp{f{!xTmoVP}%*l2$?Ooag z@~5I5orgr_vYa~5q+!_{V8q5;#{86zrd2l5?l5H~c40-7$))}lFqSqG1d4&rv*l$ma z^_G7P7+F;^0vN8{UAoSVG6`DyTzwi5F3)*Q_iLQ!!Ku^pp!R!VHgM_~X$leN9Qbnh zhM(T+`EHVdL;wmcy-e-cIDPENzM0~`?`;XY&4#kR8zO^e+b2n} z3bojDT!ly8;fw&-;yPI$NRvhQRnjk~b`ewxNK$~GB&UjWmtng_BkMu2RGjv0d=K{d z7Q1h#rMM{v!_X~aEqwGJ^WWX&da)@3Xg`^A0mO&4sCI9*KOz zkRJ-9QSfx)Uy*HW*UVk%9v7Pi^g-qMt?U0$yJSUBw}Dwbjrzx>9xh5HUWK8sjV%Tu z7R$FSG$W@)vMAU2&7gB805G9m`pvf}P?WS5tx7&$X$7ny`c0}&e z!;JiqWEr=a_CmXVRGr%V?0S%vEC8RSqhJgCPlcXaXSAPEh*MLg)+>T22R{5I9a$?yvu3)ZJ^Rc|Yz`f2= z+Esw#m0$DTsDHAC%he)x^nh>TZcU+I0YfmxfW7K)@8(QXQOe_s3d2r#`{-(Zc(T_525lZPETT_ zM?lF_wrQW`#4YC3cWvb-m&bHuzJfLGB0f*~sWjD~j!sRr)aw-X3@CxCtPUyp{{i4X zx2M%+K?S-svd*zDQVU+YugPT~Lwb7GL&9Fk-XVDH{q&*vh0_|Sc1ZqlU*Rf36`_;n z`q2|bH!fKs8NY4EfQ|^7K9#I&|2vZ@4Yn<0t^2Hd@HdTCIj4^HWfr{hL)8^Y&We$$ z?(_Ob_~`ao&pA=ykNCGlqj{YUz*bW>l23nLfQnQ_+e(B)i^0%%ufN^xFJs#FHc{2= zBDlN1t`octnK$N8%;B&1gO<1wt{9ZvohVGvVRuJ=K)-_5_grw7i83&CZ$1jV;yMK5 zqQK8|1ie!dFLA~{ayP??)<22k#1uuNwUGS9To591Y=bCTbtrD8VQ6#(8bK-rU(ET} zyx%(;bedW!`}%Qo3kB+!Id5lcVQ+ulHrC#jen#4Wip%_R()Y1oQ&)@T-3d@NHuH8L zbti0h`!XDKyhNzFufMdHN$HV7C>TA zDZ(0%O^lQ=5n>JUEII~6)*%Zl^`LD-8Ybcy?AuJ_BNP@>%ahU(`yzWNu6v8dy%guM zr_)Zb-=}jO^mB^e;ii%Fu=WWg_DKqx^R-zl{~1-x>7m_NP*t~ZV^N{PPo#vU#<8C? z+4MWaV2^1b63XwXx|7$z^Vz_`wpaG3C2qeAR0&AkOU996QRoBo2%|VIuDP?4^m7h> z6NjoInltuU#g^{pBy#B%+e)WdTSwYdBWlKy$=99LGCC7Pjd}C?IcNQq?Ua7V_k1Ny zJKm17Y<7~R;lu2XM$Kknl(&cp?aLDX?zsUkN64l~j|LwYd4N$Y@B3{mB1idI(EW9Q z@CU~r9j-57kC6){QxcVM)B=rA?L{*6(-A@* zHMaS866va8O2yzhR}M7{KFln?wz^IbU1dBE)Jy^&gVDh|a!J@rEln{9s8| zHvU3pTTO-Kn(DQ%-oMN;Rt1%wGHhDsdLK$R`6MRp8p2WzEWeGNEbYfE!<@X3J~&CM znSQ?ggl`y{?>;kL0_WCDmq+9R{hO$nt#0`4My{vxNQ@{Xfu~Z;HP5F1mWAR3^z!lw z%6SHDmtsIb-(WwZPUSs5@(TP>17T0Bbznq0pGyP=sC%(N>%MISY9tedLf}IAa#r9L z@kWq6+*oS+K|G0l3zweG6*vIB2k{PF*#-0#g+3`Jly(cYy&U9%S4e`l>#qRAF-Ug# zvQlNF4*#x_(?{P_gm@*2fu4wM`JweP4V1f?P6L!V43%2Oe;jp342BNJyYlgxGVb1r zJuX!ym65jcE)2v1Ggi);TUp|y1jA|z&*j_qi3$Pe&R1zaz6xbWO|7miZJ%iE;CRvP zMFzGR<_Z3#FCG{Gs0V59^rfB)!LU{iI*f0)YGGdD&`bvR%lR63LnJf zZh;l949-4X6-G`H7e8J{C8rz>OMSa~O(C4B@n&6NN6l6qeg}YaCF?w!djq+;o%3|| z`({`Ao+MgHN!iCScs8d6?mP!5v@t2?>_fqS6xGhBflqqPSLPLx572mp4j;H({C2Pb z-2rR>ZU)|WJMy+G8VDQk>}=0|)h^U11K5+s_2hCzbEzoZuv^NV3$3x)_H_){N@Y}r zfGQkSCGo!|kvM!FnJEgTX5#QZX=-e^Q^#5QT2>$6C5D~mv1>r z>+P~sW_ zX<1Mcyb$iRr z8sf%tElE|p2`TqjHt%t6z0yj836fdrRTdD3p?u}s+WjM3^uzitLVz$Uo*4Exb`w|W zM>yUD>&ADzl!g@iVCqL!8p;gWl360FRKBo+EJzOnM#xR`%{q5=OQp`UG?elL69Vnv*}eq zkW?lc+~K6RwC+p&s<3K9jk^%cXOfkN-{8_R-QH#{%#JaL`B%Czj}2MulRjdQn_7vZ>+!Pf;e*+@$&;K5flBt5GA@S!>1@G} z1k^EMH@fJyI#CEs)bwJ0A%trD>QjBU)pUr(ocN3}gDtLa9hK>J!H`UN#HH46EISC- zBqhtC*mo+7H#sn4A3V~lgCWU`+Us~rbu5h*ofg++Wb!0gJ=BXfgNCp11!lcU57Kh| zf;w`Fg0ttVY@y{*Fdx6}fM@-1IcA`N?#;C@3#&AkP-X_k(QaBp#s5zzvxZuyphW#r zoX!veNsMI22`3l<;d1?eldcr<-b3gw_Ztj6FD-T~^YX$7;=cq_Y2N>lueP`4n!wL_ zuUZa4Mg6^JLk!l}rwF3ob=E%NH38B`eoV3ia3p%RB_*2%xpuOxRQ&Bfz+s{3_ik_R zb}pFO-0c09KlGSDy}7~<*0tDI;CXH>cv^X%KYWOh#|_kHe;fYe`Uy<%;&}Tp&&CL) z>_?Z5#O}|5cE}&Zbcb>C)gbPyaqi(~+$*kU=HkMXPXlt{@1t@qi8Qbj{R6_-vR=`2$xuiJbh(j#=6SDOca`y}cynIQ=>egTWRNuJIiZl`z|X%rx@FOC50HhH9Gx5 zmyNKy6b(;;>EO92+{hLv|M_j0_)(3WEIGE5w9x|r-u%&K}bgz6?Fc#7O z5QtKrUK%clYeP2pyx{nqE_h!f?3mV@GK!oDXYTf0bEWsW%+1jXa=f@kA1SBR>~CNb z8cTURO|j2<@&$2M!UCkjQg?qZgrA&>TE!60?fD_H;M5{#E+M9Ogh75__&%w&+g71& zi$~kCTyNJh@FUZj4_`IjdAl1An|B80`se`VfvD-q2rC^b9FmPe-EV|&lkc#K9op^& zMLX#rbX)}6KS-?o!#rT?vGq5!rTU_DbfW>eHC;CxlGJu0@*j=7lDl`K;a$(0;Z=tA}OLnrG&=9;I+d`->b*8`?HFk`+qoH(kIS{j}@MTBHd~8KzUoGKSjK|gxXj_ciacQ z`L*nCENr1m{aWj#b`C?qkR)J%wTu`|jeb(Bm?hD``IxCic_%kP;0mMH4^M-Xd&4Fd!U|#a~NQ) zjh}HSpX8xZYmmy_4Fpf#o{6+SR2c{C{=$3Ab||gRg7T!IdxEmJWpukmi}UjvKyoa5}5GOCAMW$qeSDA z*UDfvc=eefM}9aC%*zeS!2L6(FQ#<+z+u2?Xf?W8-uAbKM<*L(e9yNoM zESDL|^!nZ!<0w+3S=gGAaF`?rwV7L2G0IbzLaYlahT`RcM3}`fG1aI_im^xvYYHd$ z=qEj#JgP7ZsXlcSp;=`Nn`o@+lJlBIK+P^BZjQvhuezl>G$^!SmVsAT!+_RXyEa&h z#@cW8^H#G$oeZ(&IEu<;OH7ts;L^AYiIhn3n-cSa%)J#1GV;`TJ-5@u<;N`t#v!^HhFesSDHoCSK- zj4udkZ7wph}<}j;DAv$*#Z|H+aOJ=*1Bk zA9-t~{IkJ`nb`>TaWSWkT{JHYt!Mnds3`7>?k^6&q_`D4my!ej6g-?m=TFTKkY6*g z4RPZ`QSE%cIhELH^I+(D=+jhA_@`@WnW=mIm}ZFkTf*ZL8AdIgwAOQ?IxA2eh4fTz zd9uuMxxNf{j<|L(W7EXY)z99!b|W_cRe2q8%_KP&)LIF@6{hey$0;s8p|5Pz)-;o6 zPPGIJ)KyydLLa9WX2?MAyojY(pgwkKJ|-^rQSc~Yn}#ApCx+q@wu`v8>^v-IDPaX^ zqB4{5eGjC79-PBWe=o&F@VSGWL~Dhp%Mca?K4d>4h}%W7os;0Hm9LWJ0F%$7J^#4?Y|o_YFsmM+}#(l&yzQyVQ0jc z5%w&3AT?TVZnztsmyoU&gC$N=IA!H(^2M0cU z+M*rz6GEW)8_;?S`TG{UxXjmN#DoQJWgmfbinw=--i$Qv0>G;X;bjUbn|YLNv+T1vN_avkDk>eEQqMc?s*^o95O4XnP^zvhiuPE z?1*qX0Q>`-RxC80A;q&DTAa@IW7OC+I`v8o`(FYly1K?OzNs}nmhgLJX>dZwH$Q%w z(7bdl+FQc&Cf)oR%-PJpk@LgUi`DQE_g?_{m0DDeGrxm*%LR?;___~X zdYK^$62P7|qH9oy_VtUE!YlDIf4dLZQu&kHvUm9<_w|v<+`(ZWF-=o{>l2NenRCtp zY2iG?uK^DO#(D2MWgB7h;irWFQ5?Rb2ZI-TjqGHJvJzX)qTUH&e>&on_|2R=L(T@k z8P!HT!|^L`TNUvtw3d4(^h5WpSL#6XC|dUlool8ex=*A)gZRWT?fEqE5(R{Sg~l6& zD+z4}Di6(qE~Hj7S6X!@@1O1nVwFG*@}}%z!rigt_veM%%>{9V!GmMhK_HOv0BoTy zAe8OfNep6sH4>=4T@)bmLKnnt5eLqY!CZ?Vii?f(okw8qIwAdSUspt5D=}tsO!t;* z6l5Y9Ec*Eax1&t^c+Z6U z0|U>#z#QGruV%N1nH(Yw-aP8p1ZMJvpojD5KVYazy3H}ZXBqstG_+^KaI4a*%I;gQKC9Uy;KRKP zkR`vZ5EoeAp##@A?T?{fa>t=oPUQ0ib~btj#2LN|LH_C~iaNsMGoXl+P#?OgNS>7Y zaC|*}S$Vrq;+yy4h0aBO54^DXJpyUzRBvP00{>x+guVf@XVd{>WE)OzwUTh4`rS3a zGW5J*mIpN6@cw;15^!bn`%tyYWP;D?7vQtx_nXZxyA~@HshLwH@WZ}bSV+zM>kpc^ zhX=61pVZM_hp*BIs0W+X4D0MtQ5&v0^BEb7^e5Fl zwCU>1I|Ov=JbxK+*eP_atxJJkbf3s0-#Bg0cR&^?Fo8}IJP&&(^1D6`@xpynQ$g zra{CKmn+ufmM0NLggnwb7QX3f(}Sr8IQIO1KeAIfZeeC*LKu^Wr+3Dr3%JiW97t}h zIDCCVmC4TxwND}JwS`ro4W78kgFNX8Yn$nxNv1dZ~wg8PAnSQ#rqKXHX`MeO(?IH}O{CGTY%5r3HtH?&5?65*HI+6FP?5?!R6K-InhWJ#b zaSwxu5yB}n!?>;)r4^GF%H^TO>+dkHaj*Xt`EHG<`wE=b?`SN$&Qm7F>4r^Ug~*pj z##`pS*|RwxN>BWgWjBft-j_)1`{?=>JtF)<$@|6}Rr| zeAKbzA?K(LqxA78gN&#sZej=D)ktl6VKO8wz?eC)6+8Fy8d-t2ezjBKfTs~5=i=xo z(Wdc)fAvo=W=qt}g9q&`LF11}_5>bHQh0?D%i7VY^HPt0Vh@vOhv?&{)YKRCv#MdD zMN$qAMiTrg0NS2o&6Qf4gCsQ7s0~l!W61d}9=}}864i8#?ff%oEcH3J$XLy#k0nwE zQ1Fuzfhq^m={LpmD(UUgiYlsC{hrH~JF+}wkL-r9bp)x(wCP32vNm_$V6K2cclC~A zvna{%nixhxw%AiSI<~(WmuejUttWDv$y&L*PNvsH4$-ccV7~2NFKc+k%bRDj;|lDq z^Nu9S+gcaplFb}ccfDyc#67fAe*u9NK(V`3$G#fXs#@{$&vKJMjOUFX9)0akMpK}j zhY!mTAjI(Mqz);K>-@h5VO(%y?dZ|R$~}O03*^82FlJ|mEBq!r1b(i3(_#T;HUXyt zbNDySsxdKOtCQjSguO1^;O!bYoqmzw^A5mFBP6_TMtqgVtB7YI2OE>ZhBG^=pigy; zE6%-d8t&e&MA^!nGJ*;@*Mt+ar9o9nqA&}D_n?Bv+s^5wLUNZs%C*_N#iW&kxQexs+m5AV6j zgryx=Av3ya(03Jrf5~qBn{r~VX#7CSGnSO^xT@wCOyf1uZ_t9TeWleRd&;`vHm`Z- zpEY{c@V?uv{ObzYhN%|ffkyA2h3|FX@Y))r>mH~91(uQQfpnDMFq23Z)ZqUMeb^mP z-d7+s=@+y=#g{*Uya$)_D{%WNi4D|Z#02jzci+DnoFm}+(C=Swt7*}IfFl{1Gm349 zHFr($`1h;zI=a=3=MtZv{sY3uA4qP)G7a0clqx@vgm%hHmyJHAXcPK~tnb(C!*A1# zjku-0q%ucMKK4d78g0JRVm)jyjbXkE0&~D@(zo2&n+rhiu|LaE>|=8Lzs8Ej-=t8l zH~L*hRkT72Wm&0d<6oxcKLv6nC69r;0+Gr6r*lC|2faW41Ux>1nr1Hx#rf}tZN-3A zr)h@?77T-qA}1lh?Bxy;&ahKYZ%@m={Kj&|>OZlb)0eGwxt{~h9P0TFD3p0p1%clN!yaN@VV<62mjO;AP^Qii{IDe zPKU=LS&H!%Y%1Tu4P< zb$OVQ#ELbb5~Fsw8oK?3>MD?qZVvYtpNd~c3x#P{tj!?ab24nU1X?Lz!fZ8r*`CiS z>iL{X&OQEps=pmdZ_~>^=3i6&yiu+@Sth(ZUkUAID9$pXh|banohg!|D>iwWj^#zA zdHe)etv!wWGNf6U76^{7b>k$-7kxa0N0YIDjuRiH^Mnec`1diUf^@Fv->%Urcui*B z>;LFv<1l{>>a(y(J)TGZ22>tmej4Gt6)8%!%8UER z)q8!{#XnC=Lh?)~T_KO98X$(Sp1wJRR#OKC^@B~G>zA5Swck3QE1hlYetI^kyCfCR z2YBjl7zVq+OZ0kRe~oSsTS@jMM_Qtwpv{>g02EWoBbv*T`7bv!C1gZDv-XrNd6|_MWObK(+-#mGkBn*kVK{QF0arW|ED? zGH9mty5!Pja0q?I)_peU;c!1E9ydd|&0}NO4QYAovFSLymP!8HA*B=$8eh>&?yrA5 zaRYJP zc0c-*-s}*avMBv+`IoboNAq%TlbhIyIwZs^@~hV*32@z{llm1Gw>d8L{4UW)d^28d zJ74+`b0M91$#^COhszF83rY2PBeXxL@H=b0uqH2syWK4JM~E9uMPJ8D-mwe#*>9|k zylMr%@V6%j1V+VZ+e}O$fL{^Zb&`jslz0l5OQkfleEL_P5`*<3you6wU?@n<>0h#R z5pCweW#;3)O)3oA!UW>0DitkqjDBPhEw|q#vlbE*vCxhufhL|~02e?D2>-4e1!&JJm z9GKbeYq0rVnVFS|>iv4TQfvCjN5_Li4J))5VWJi_+mmuFH~B?cd_>GyM=Pg*Cx<-L zla2?0cWeFvZf2;lBd@I0Tb?*aH&6+i=`WhnA5V|u4TAL%VrUsW#dyDA7Py5W(d10U z8thYA-TBDK+x>|iYruSby;sex>ye@UJDbj_scUSwjxX)dX3AM4(paSX(L@iOu3gjU#pg5v6@6j=A~%9D6^Q}aS4hOINP!wlW5nT z2q^lWS%nrbnVIF*;vLp*F5O4Re;>@(#Yz2GoRLA-7*MezHV7ya6~4q-XnOqA=;X1c zr%R}dw)-2wEzNBGNdiB7Q!jG2xAGvvcY=0x1{+SV)=@O+tgT*>HY92j(%7xqgk9de zkXkX+GP55++Y2*a#bIG_obj`*c9=PKjnl}c9p3VIaCk8)qJJ>8-qHxAwDo>cbaYjZ@2J7HSYF`R zd^keAg{O5^Dfy{83&B7h(`2I!uwUwY4-UViRchEqP!Vs}7WqD?6Lg<0MeWj96EryI zY*4||NCRLoaElN^FPrd@Ut>Q}c0xK34yymkkZy_k>lsv!yW4hTfxu^&Qmtda&N&0u zWnemWJ?SHY_fX}m4My>FU z<)4pE?4-liYKvFFHIk9jgC!=?FYU(USt3LN)34c57%pBym){8^jhuV#%~{r`f5IWU zeS`qwl4p(3^;a?BCOGBPxv($cqTw<>b%tM2-JB>Y)O#<)DJH>i9k<|RImo!B=fz=l zt%R4u2)6$4a?~?iJ?j4D5HN?~mxAMwrdz0tc{m#4Q5zh%T+{??9$H1$$E!-?=VrmVxPRTCpf}T3-tmrP z?eag|YxVYLCpUeQutejGQ+lE?oY(7;$3Vhmb4%S6+qYNq!%#dJ_u5#Zg^_CGN5(v& zjryvNr79@;MUtnR18q{(`!R>_`qggo9;XSf_N)kxWNToC%>*egIxv@q`4RrK$etD0 zS38UG)n8eu`Js;38W&qm?>qKUvPpE_@#AqA&P&>Kd@P%zk!I3~ukq98FkZ6WXMI|J zu_X(rhOLRVbx8-Ft(}Ib3sDxnB?5&%e%VVzRWXHyGC$pBB_DH)SnP*m{6&so-)5>} zSIE_~+Ai6GnHEY23@&Wjm}?^D!V`J{B3jV~bPaUuY&U)?V=pluNdoQXAURG+8_|!v z^SY>G#_@T@(e;oFpKY+}+mh=wheshs?duStt$sjWe;JD-UPYMx&Kk3o8Hjamfq|;< z>)e;6TEa9RyHRWphUp}cD@ft{2*JooXQhc<0nH9#I-e`*QFwJ6&&Z^5guL7|Aabw_ zi}RYehUcT14JH5XEJfoAKR2TOqpL-$d8L-Imd;M&(XY93%vWPZCwbR;{-VvIxV&Vs z{fMC9D}3jVKOO;BSWsCur5O6JuR&<$e*@{AR=3G{FN~F?9c-hY7S4sFR`0(!Ly6PWU_Om&=koK0nWi9gI#=*!Bm{5u#VE%u|A z;$Apzwc)5g86Tl=bBw4cvBXi~{d9uk$lQ?xH;Zd#VH6|NAI+T<78(7{WK!fIZkPF3 z9{WofA$p6iFDS$uUlDUOrOfWZoNfTc2D?&tj;~*!$&> ziWhUmCo?OaPy!|CSWm|hQ2n`%7aRB&eh>;~+Q~|9%O!puwuzYmbCVR*h z9zpYw)e~Y0<_aJVO~$oQ#Qfs`g%W9-A~N2}@^$p-lK3H?HJaUtEzxOq_cAjH$N0IS-89rV{!VLzy@h*XUnU{C>c9BUa3UcJw-r@9>Vo0*|sp9`6YFp1qqZ~#- zl5!MK!MP6x#S=_pLJOFR{Ev&DOhtxS1yWO2VBA*~gJ7{>&KUYAahU%kyeGT$QNn3| zvJ483E_h%e5y4zNac~~%keq|@WT1GcK&UR?;Nudbp1VG)9^XC^Y$Q$0Je+3@Hw_MHBE|HV-K>pNOpv-gla>N=Us$q91uLXRo$`ps7#A_8y?C6aSb` zwgej0zD>A_j7-H)`{_1!@0UoQ1KRm3zsqb>}wGfM6xce%cq;~HQzXvxB?d1M2yOWG>r%3OOjYG$@?Hs-$i# z;BGDd<@1MQK{|xZ*Uz@A33vU zeJ>7o%$L0^?v#Vb2zXoiZFGGN{(3kc{sb256!*U7auOO@7`!eDK{?_x)-tFK<%-|% z&T6ttK2SbSaC}%K4)pW;?Ia{vi$#cG-<1AP@}8rJ4{P=q+$pW;eyO18j8s~Rl*NM$ zGL{as74epo=6ZzKJ*8vWPtDn~Pxt#?qT80Tl3A5TtsBF(;MyGPvONwMxrwYd_>FXs z-+tfTw?~0G#NQ&Iz&=n-w%q=ah=yZ>`N}-GoHjvvc8arWVV%*F6wXmieBRm{$wj^&jyJdOtKuGMmowT#ce)x-W*rf{^f@*xoksJ ze|N9L82%AzxxL6M3a6de)$Ug>VG0H@;Eh)K; zPu2B%AuKzHJ`;AI{@VW+?W}pqR%pL}$9tj z)tg6aE86n584y_Yw|6K`@8duhKbKKr#=kZ2OcZWB8EmUQI0ChbUhXA!d+DL1FxkHb zsldci8sk6~BRmXBh)BO>vkRSin=GF<^o`kB{ql}S4+@Rwx|cLaSBlWYbum2dXPyjc z8a1N2reBcoku+p{nhJX;%g$)q2G;9rZ_12rB`+&gcpLo;=4YlS#WAC7*aYvjiEp@n=I%*;HVvRyrfu+r6uW1;;3mKnv;+7nI+#tqbrM<$rsv;U$QJpgv_I z78zxQ?0J^k1ZqRbN5n{b;Q#d-Ep$&%sI{<7IA$}P(U{$ta2pGO z9+_h6VPl8)B?0ltx&BI_&W(+(BE)?^>^R| ziT~;?%3Z$seOaXm3Zc()H^W(ZYzA4h5sE;Y6*N(qn9g!nk}@OUDoaH8c){v)2@k!Ai z2k#%5I@ZK_Um=};K(HSB(VfP{iHI>AH#~L5K0Vva+I<=Lb#r>xv8>mFS6z=3G8TcW zE47uxLNl7Uwn~-~Mgxee&>~tt4j1fggxL;7D>t89VY+I0)ts%6EY&HksJh%$NNwX= z{ag??Rz!s^d%)b`UbyVGSv%9G$K?p+#&2JNg7dLD%9Id*z}aG;D#55sM_Qm#{m@Qf zbndnj5Ko@^vycP*L^n}UzbH5ix|>MqAIFxD2KJuah$OFyNd~}D@3HhzWB23fMTRc} z9mV$@JS`Yi?>(3yMtWa|%>ek|z^ucV-Fq}|23Xm+ftH(rB?>qss(2n8+^v5 z8TpYPh}UP1ib1p0i6+-0Tw!VfnZK+iqNk12E^itCvI=s| z4z(hZNi>OJrEmc#c&-q2ZIt;h2@_+)|S#*v^zPB>_+^qgOB|#Ibp#_~c_G z@u^aiTkHt60s_kGDqqRS#}g_}zc%WHp=Gu`c6CgN(^hnZp0D={u83G<8HqL(k{1bk z_+s!$BKLiov##okYq|KvLDw(M%8%J7FiK7LDgHzMvFa?4fYCxgs9P~16QLk_JU$xJ zlPQ3hw+%Hk&%E1c-?3FV!Ht8D6j9lkYw~NDl0$iAyv1fwHh5FAu~Xv*P1;Mt5ml!F zHGi~@K9NJPp!#;6!KZYte5`PhMyESEgKxfn1*s zjcBqKHoi^O@puj7*|{j7ILOxur|`MHCbGw(0Z@MHIY3SBC%2NKE~F5QmyYe@QAT}= z*2D}!Chej4-g^%z5uQ2ZVj4G)_#53)*05dW0B(GWm|F|lEoApM+rE^=Zxopv9LsQ( zJU$)jvRV7>?=>pqc}3KMPiC34134k2@GddW3mJBKu9Q$~7)g>P{(Q;mGatS57?~6> zr~o3-BF4S`aIJI|1q{Tj299!{gV9^(Ji4;I?i?2?qQ$>KTU$-yEFoB{O+JIW%f=!| zrA#x$Up~5(p0q_lR;&i|H#D$apY7U(r@()sGSlkc{ciuAasf(g&!#OZ?9CXn;bK0b zSuiAQ?H3u2_lm5N+z90NpY5cIX?Nmbwt;{XBV+eq*Xla-kE$k4)v|eN^Rn-x&0$7F zVdVSvxK;_{O+vo6l>YgPWLoI;LSYVEeEj=W>#vT4$!X^MERET{7L43i9S(?b>mO}< z2^P#S;a(?ov15AzI`WE}Bd3Yky&>mnoqK2=f(CEA4NZ&^sU0wFwj^d!0~YA}QgB07uqEF`{tR1sFp6G9-z0I%AY7B(R)T?C_XpKc=dp_?-hoz3fdx zFLHs%nAFfg;-7O9nl$iV_jy4>CYC5`A5>Ft`A;>(KpABA@gHbA=-Aaj7#iYrGcVVh7u#23?_KDGG zbD~8uPYl|+iY4x2Bp-aFtq?9uImTNV-z0i4*xsN`d)1p}_LeVQ3i1wtTt-u|;A0Y< zIINTM{{Fy*KYE#yD0Q)6$hB653C5?l`7v^dU3zFj_bmE*>8X?GCKk%QfWoh_sxSsoivW$OUJjM;_?@`TUtr-+cR`kpyR1`X_ z?z;YEdBj1Rr5w1Q#AXB-Kwpcc7Uej;)34iQy9U*wOxh zo=;2WGDA0V4O;KeSe=BcWqfVW`L0?abcYAwp^qM5_E%0TAtU<%P}!ILB&7Itj!{y! zLvv@M_?k0^HatGb`4=K-No*=6t)17H-kS#Nyxi9Xarf}Wu++{U^KG_-2*L zD5Nq~npm+pYsic225_3-+obxlRL8r?FrhnMH1|gXFWAsfVS5LOumCpHB zr(I!84#oXacS+8_m^$uY0sBjRtGe3bFDR8fZ+_D+wI_zo>;ztP3#~nn`@4Xl-rb3C z)rn0PD75@KW%!g$8_eyos}n{vu2W>F(c?t7PuIE7KP8+|3Sea50jpZNB8X@;)l;Do zhIa}zECtbP5}e*q~>yK8z;5C>s2~b0RNrvJ3k4{DNzjP`OETkR@on9$W zp!DlG6_5wm-Nr0KME_8ecpjUNt}3`Ns0!_5kT}2YJg`diFGlLIPba5r0{z3M!&kg74fyR+!;eB#d4# zWHETe`KFag&c4!{(`AgzsEF1f7gt1#wn`In0TbaVU*VvF<^2a#flo87L=^}&@J%^?cO;~txUj|+Yg`ss=UIJdZxO(}QstEkpvgV&ZA(xAUrX@&M5`MUuG@V+z z6fj(`@Z+cR^5N4;Npzs89Y7WJ@Sb8DxEFibpNC#}5_5NnM;?B|bF)`Hs;NR6T=Fz+ z_eH$2;n~uLnt=+K<;sVEXYW2O-_@x-0lGPe>&uF23@V*ohly&4M|ZlVu+Qj`{S?O( z4S!&cpVwGgJ^O3hjRZ)wC*lxLNW{&v6bcZ0IjSqsLcFW_eGpZwt8GG7Gwd!&vECHr zj<_dqbZU0f5`PRfX}aT|YEv)fJ+k!eT0{Qsjj7=EgM4#KYa6xL-Tr=nSG0J114J4t z6n%EbYfuFq!ch{ibFs~0c)?4BG=DtanA$QEt3E3@+Q*j~o}amM9`-xYK`cIQbXE3n zXKaJi44;Et(aOD-coutRNSek;nxc#Y)X;IOH{h6dL!Ge7F~j95I#P3BLx1Zy4Xm^@ zi_HA#Qd}M9zmV8zVM0myZKRc|13V4t-}t;wvFI8x4GHIkR&;ZyEjd2Z#nXRgy40u` z6vX)%DdJ(h2x*FRxXtT?hDYW{_8s_QP1|FSla1)aNpL0^1dH;)Z|>uO zo%J04#(sIp9UIqiq406qUF?L39MZ)hsz?w^kILlyN$E!jhf!sy5PoqkC}5(cspLIW zF~hl7yTFi-a)xe7uRQ~|*OZE|P?J7**;$zSp5PQA2`2W&l$uCR1ig(!era=sE5YwB z-)|c7;22o=^m@K*8VmY;eA+07if7#X-$NkO6R=TyW9-{dXY{K49`X<>2n{)U2O7O= zI{=QC*7>724OmjKRoUHi9XNoeu6-^k3KO)O2i?0 zK^3U01>Z>F;^1w153**|!GKR2KPrM?z55;x`bGt5>wczBOKb}m(bB?a9n!o(-H$r$ z430+q4H<{G(v)h#D?jPI$6Et!F>8>R~@WUn!& z5Gp_BEINxsQpcs;7{U3jdo>3mXT_FZV=@5}#L+`zXngD@7NI8%1``vrr8*jY#&mPIi$T(;0efBDI?KS7j zE%#Fou{gymUu5dAHILt(x_-ZPPCDxuc_-(w$K2`;CVDmGAuMXKL}I0!?DDXPUl}>S z^CtXPjTzge4l#V8{+!%z|6NV*$eN?^IH``jVis~W;b457=-huY6N&*`k7k>tRwRP` ze^@r#La$GC_fLvW&%DPaeYx_zXOH?ayyUeNl!I2pj^(PW&fo*r)X-LyhqZP&FZ1&k zE~z~IeJOW#s0_#A^WYD?$JLo;F&eBvo9hp9l)L7@dn@Y~_0rt8E~S5pd&d2hWF9!b ztEl+>eRBV8HUhQt(D@M$8;W8$wuVAf`c7S8_2MZ7&VE(bu5XW5BH2A(^w5|6`yz6OzOsn;WA9RS+HD|0)U5j3XB>kFOxL6ZocLp(mqQ!i}{FMQJ zO|^Gj-h>GzFVPQ)_MArcNU!b{#O&!zl{V=;7jGDw z`CJv?_L}S?gi~$4dBL~vUEIN@eI@uLl z?V!L}nR_B`5S%x=xbrF)>KZogTgCCaS$mu9|1ku1nk*FO9Yvcg{OtNqsjK_{W zx$}mq{8NC}$CDp?D-Yj()pw0US^N zln!-a?9cJ1#tna6iup|gkEz(fYv>n{m5R~2g``Ulaa(8TbYiY*9Im>-lnY- z`aoXSO1*%bX@9DQ=1~r~Om@?@Rehk;79G!hGQTMBUF@05rk~Km=p2#)Fufh6ISlCt zPqi>f!ZpO{0J(skb?>*dC`+?Fd~I7uSv#|qa;ghd{$X@!XWU^BF|?n%a^7Okn1fNe zTV)gf_62(1w@65~DzlZCp)e}Q1kw`@b3fcE#3I0k(Ig*wq*_CihGv>P;TQo153-~y z*mxSs?9iS%^14PIdB9fhNuUPZ0r0``F9ehL~_^8p^$b0)>POpdNXtC57A zDSvM> zjw6RV-2>!y%Y6orQ{hh6RW|EcDz`8#RWq^=Hv!PRfOa6f&%-1sCFg$k%ej{Fp{>7{ zgZcqNI}9s1Nbe!B7)HO7aT=&ewqzV1en67jjnN^mxb-;II_bvbQlrAMc-i_rg}(+C zsmSs0^!}A*Hiu8XTbCUP`gu$U5uXanQSY29?DmTPODDe@6(cZpZx5|7qeWlfbTt)n zSVEe^XX=H`xpn)z8;c%ZbDS33=Riuz2m(Pe%(=+LIPcezq`j~Wc3JwIZ;GZG&MB;1 zMp@{Vcu1`S^4q4W>b?6MNO+9XYwfKxdX*@teQKL>?T@y{V8A+9weF|)yhJ#O?HeGe!?9gQqt(T$19%_Pp$#Hgf}>w1V}07 z>F6HqfXNjxlHYWATD(mCL0wBruD3A;>)oKgzauWp>CXLk?nbJ$$7+D1%X!^80Uo^n zCTb%4*y*%><&Y=veJRXzMUubCVMnS|{K)9L=ZxTZrsNZND%#d%Sl&S%qlKg&zMjlh zC_3oTad?32PfJ|6$pE)N+>gr--&|=N^6~tDHhMfnVVM0_6zc zZmy|vb&kyPk`(EcHL_dojaa&PJDDtKkkFe(MCinv2-2&c@jdD`mCejzW(!CViy@o{5q7VwR7Vb^3-h%ODWWTn?k zYz`zZPs}aA!~%YHSdEq;;~A6gk?`ryEnK+4IbPaCJW({_i;ZFj7J>xM`h)wx@K(0h zq$}y*ST=(oxwPUI5;P1VkEuKwLQ&N4o6lq~vHEa5q+m)II>VeVZL#ujTIBhRzldAz z_m|mZ_vQvtVdF<6<)D*O2|45v_`F`U>8}0K6 zo1r4bvz}ftOB@!_6Y}2=@fs}rtR3sZOR?Gw7Cs|mxsj@xLH zC3QOfyjKiW@E+Nc?lNjo>CQl6$kQ|nVClD*ysf|kPj;)vO~C>dqNTs;Di2NM?Hr0) z8;{0+z>vm(+mwUDCq7msO%d1q-!7@XrB=jxRn4_mbN+&ICjKgmQN3JOE#I_vCd7 ztO{dQs13O`7xMh?`>BUNLXdG=byYhG?AVYfQCqL@XAw;-nY!Otqk}}+E=4v|KNs=( zra4NTqB9wMV|C@#Lw1WAZ_K*4Yw?tgo66$b?seO~>Bl37u|SBq-UAF7VET8;3?fcJ zKk`|_N)@)0>dydp-4+YftB_!J#XH-e!P)giY3-^Hg#7->8jDpBw=a2EQv!yH1GcZ`j1D}AcM8Jp6&vJk1S(+i|&@_uX#A;2Y zXui_#kEW}fq~-O{Ebl^gSv%@5ipWnp)OJ3-!Dd4grK#wz*Gc$=MFjg?Bc3y# z!}H(eThHUohA;S@%hAG}^Q-H&WXk*Ck#6v86S@%ZtNoaa&}9`O;9>kK#Mpo5KnxE% z|9~C9)eD|GUN*S=<}EIfbTiW_YW*odKKq{I=sF*6fYj%NsdihUwYFJC4F%WM4bTuxRpp zTbLoA3&qgo#KC5HHebC@E8{HZsn-&$RssUP{#?z#9Vnx&xw)Okh4#jHDc+lpLQ ztUQHL)$Oord`WKolJOuvq}Juyc0JH`?ovw>eDp?qCDn5S*;^k_%oQ}0+tM|xdRMMH zs9Jy zMI+0i;QNI2`Gkqw$9}PTT0=YlmGFW+fHpbu07g81d60fc$aBp7JoEaT=OMUu9|~gBq?ZANd=b&m z;Sk!SqciKfBbAei#XRTL+`Xza#q!z>x?9})Fw?VN(hY3*&+Aj{rqT7!>k|ud5{X## z*|+}2k5YaGC)9_{t-qHYj8=WTj)$4#7x4$~LnF`lkN%*bn;x&m>34kENcL?ok$UB! z_n>ql=hKfACdgVLcer6#ttQy_m#N!yXI%_FY)TCM}+L7L~K+x7X!W>ur@Zl?g@IU zb`W!KlM%r_;Zt2xh}i;9_x8;htS0aWOz~a1x*a%j zJQmv!-RG(lk*=;z?raE*KK`-r&rb}eBfu_k{NxLv>A{hw<5cM{lTwh+O0~6m9wWA} z&r+7s@bX381~QugQBNQ3JZ><=N*ad34M=L+keW)=r7J`;lPoA{VkXt(XxGJswqO>S z4R5nW6avi)!Ig1MDqb1s>ZRv{9f{+9HJyZmcexw48}3AwHB~d`z4ylQ_ui=0x75ga zP#8Pe2w9jY42%YxgMtia-k|qdD>Gckc`|0-#%gXAokdUjIJVeLP)crDjiz2f`s$5YP&Bysj*pJlQQ$^ie-)e(#3! zo7~{%vAiZ+pUYp5hr&g>DDD8qvIo~3kZ}U-}Svjf%~8>^fuPtJJMciBuv zH2;xzb>5M+wsB{dyV7nebTTitItAuI44r zf}R>V*XhKfL4l2~FX+4PSss`Ve7&1`SFu@FKI}3_P0ps+eZDF{UDi4~@Iw5btOD9D zRbxoq`CQY37e{mUE3mO>TII#79IA)!_=04VY6@LhMy3RG3VFA#q-$oxVyfqNcRUNu z{SrnP5a01d$|(8HODcSVMHvUbXImK%VsN{0!!zu43!(43T53hgH&g3s$F8*?mg0-A z-R{d&nM5b$aQq%Q1cbuejO?P{s|d4vKB3&p`*osWZ`ZUcw_$^wiwP7}!HkmXG@8Fl zuHGkPe;Im~$^d?7>=Q&z2C8VhY<05>1)663C(5Wo=PO2t8U6ROo9H1&;-3XxbJyct zMxq4&Ab$k@QSueDx}$yH5iX!gD01Gf`HfKF<@uaIHmtoa1}GhH$UaavFk;idqb~f4 z%pYHt$sA5|T7i4lbc1$fZYrC?$hYbSs~%-Pw!RJbb0(A};*0u>iJ3J$S_oNpVS+OkWvtGCOhX=zpPfc^qIi9t-Jk>wCW_!5nQBVDx#o z*U(GlDRF6mR#IiD}B-|CbX`-~v zS?>7=VIYxH$(#^s+}GfYI6C`-%hTy?H8^i$Dz4K&);|E%lyd0bx(4k^B2<+Ay|~0r zujTG{^PQ}2&#hPspOiDbZkw?#&A%Zx$5(i;=fq(oLv$)@tDPt~XZwEboRUtP`^Udm zgVr-I2D)Blwa9qNE3o&uY-5o@VUw25NY~>)PsLU+)!|F(XZ^ZP5OxhHt@iAgP@X6n zlptP;mtpn>PQ!Cw(xywt1Q%Ewcxr>Ot$PtuWsCD?SIZOR+wmt%}tB2hL$eS*OT+l zv6~#wO3r8u+UC&M6bZ`5h)k$zqSDu&H`r7eQK+i{@>2F}2k&{jQEjxJTTj;g(d%?v zZgTJa@1|XjK}OSX2wd<&%F0{BUEo=}72MOG8w5m^7@KUO^#cwmSIr$t${b_y#lo)g z3F*c5y={4O<6ydqU|rnTH!7U8V!jF$Trts28@-gEOUmO;N%e5p6@?^Gk@ zdeL?OJEc-+ak9*4V%`vUqOxkaq@^_GL;SfZ6yBO5f}~yc1pVQM1xHRFh3EO#x0>No z5F2jMieJZWb|D$}wl9|!tsi(Y<3C813|>^K69Han0Tn8w%T z#psxi{!N95Cg0-oybvh2Vn)qUmDLj0$I4lpb@#PH#T6qG`p-ZE35`D_Qa<7+mr=Z- z7VG2L&RkqF(p=crlI%D(e|_U~s4|9LStTt|D74pN-FD^8SzfdDaro%lioQ%5C+B&) zqjvzOVpK=|7&z~BL;a!?F-&`%-T16WSy=EMH&`VaQkwHM-GB6|d^=m^e(T;Py@OA% zKE@vTpoGS(jy9RK-O2%m1&0$o)l9f*vXimnIO9i+OqPb&$A?ITL|GpjMwc2o+&PGi z1OL6BzRVQ2X0PrPvNN53vOK*J3sk@w=}rLdMA=Mieq3Taaj*_1vmoQDm|iHGjLi;U zKJBXS$#}Pg%FtRPb>lS@t_EA1XQUvD&Mp+Q#bfi2xU@%7%E)R zU+Qhwnk!)MV-xQO{0y^i>Yh2NzM#l?zTt76$bCsEhP?2W zShMSYNaMkVJGZVMy}TIOpdsqx8@bt;K~H<^r63j?w(VM**+5+PcRTVWB3DImmuD~C zTo>uw5Yw$6|L|FHCZAk$_>~rA=Pd?onN>nxQeDM8txzII);^hv7~a<4Jx+_5j{1&W zU4X(ha@_Enf_kLov3h-zBODX1FGL_-GH|Eh;QT4!=16FTj6$>h{U%=@SB2(~UA@C6 zRF?j~YXvI`PO#9TT8-1@c0pZ@5=;1D6Ch&T`{NVfM2b_1DUa=+?b4r8D+M}bJ0S!S z(|UQUz#<=GE<#26`OsSVsc1gNslyQhUT5T7(!s;|o0TVNk`||kXTA%Sw~i{f$F&T8 zY!#chkeA&;o9^^IAMzMPe#?Ygjm#09hD zbsMzIPc?>oE4RJNs}SRM!20#%GI=4Ym6KMv#*5ti6+GTL(1`+L;Tb~|mhi{J6gTPa zo`AleDMBcTn6X5VAO3ZS!tFW-3E_YRo3GOBv7G>d{3S|o?O_TrxZ5xV!~Y(DLr`E5 z>~A~mei9&N@hU|tkRzuFk&p2$0gtWy?c6UCfYJ(R7I++X3LqSM`}$3KW(cl=jH72K zW=}MjX3ij72sJw?C^_t0Vf<2{&;YTLEx$=_u9ys&XEQBb0eP|Z~U$>(2&9L4dLFXPno5srNcaL4vl%MzAW~45pLWOYNt!& zP~jb6I#a+&ew<^**#3@(_E*nKX7{OMGlsuAoVIG0st!b-y7YrwPGvp znjNMmVvIIdMP3jzV6DFpk?2H*^()SKNe9RqarC6{-x9XQ#o>t|8zLx!IHm@(*5dxQ zxvOQeTk9e2unoJo5i3EHfxkiwO-+^K5&b7-Y=t~OQ~8dVc{K<@_+RC0 zHMT`I&7lj7B!uoX0EnI~_36B`51S+vGW7C4Rf8ZeE z@g)=xfAB%$AEavPFO}{d&gaKs$J`j260~x6_qXc?I)Mj*XIQ|`V+6g&*zeNM-c2Kz zTI1g4X;MNA9n}hYby|SdC9pAGLQyCqFt4$qV|p;wG=2b5(BrKuyF{7UsH%>mN+swT zLeCCkoqsw!d?a>xu%x!2odW^hH4Q0+K8?Y!v~hj zJjEofdZ72wqKNaHQlHAUOh$EpM?-kf+3^qU0F=P=4~n)=g#N`(OvYXd z@})NUlY&M3Maoow30Y*!lOsUcpw#nI2W|5KlqL50rqF36aew;q!R*toRvnjx)Lob~ zKY2YZMOkha%clC1W-lb!8lE!B(WBl|W_r(4!qlktVOCv62Kc7 zAj}SW1{1x+r=JDZya#=19L@^@PhT(XzvL>653y%jO-FO)JQDjUThxO>`y+8z++F|>#dV&l66c_m*++M zPp7xj8cM;Zx@mKchSXh}^>LgB8oHGo*1bFaBQJReZSW%EL!Gb9$NMYrn!1{v*!3(} zQEA%DZr@azXPPI&??>3|cZvP5@wq8)VxixzClQLi-6R6oJXn44L4YaI`bF0@ZPZWM zxu+!zi~+EXa+<9)TqpB@TF5;IWhDR68=Sf2vhLX~J6RXJqDW4yD(PD+> ze2Ttc0^q=T4gJ?+*-WcmGSaMOxF(-umBh168x+>1kqo9+VdCiyRcZY6l|a_@-UJz&dh5>y@z7M zY}qL^>#pz>S;ebP@%F$(JF}wDf?~=8@4#w{e!c_uF7pc;cOUM=2RO)NdVkWV2uL?K z*%lJ*v8AWYB(e~UzwDEzyr-sLmQMN!ad1@VF|~dOrgAWMOMI?xvKlw1D)Y#wY6`)e zGSt*R@;dwEN_L4^YPBV*J`pj8%YLQ9v3}{3Q$---Vslg> zBu%D19pOSm%?X9FgQaTzU-!VM{dDl?(*DAiroYd;FaKynK6??Dbmb+QWjIRnar@t5 zBU+guy>ykUhZ^KZR2fN0DaPzw$_|HNo?V{%EZuV{o(mc_^ZTZbKqX+$5b@uwRxmw!tY6`8+t3s-vHdFf=lIB-r$L)BT9JlA^swR?>4xZ?iK3%Z&uc|39zI;nYxrNO zFTGLxwtuf8s5n_eimPeR-vZD-j8jWvtsZ+m)C&%D^53{F0EEL!rq3jIS2(Z!wLzWE zc=r=t6QM4Al(TQaQiHo>t+QrxP=yS^pBebG-hMAK)5r4z2j>M}r8hAVm*kf~fdsw_ zWY;QC?iX1w;G`XkSph6%hiET9jv>|bdCF&C5$yW_#E9%_w*PBU#I}<6zS>Dx?+c%{ zQX#=D`s2cend;l*P`J_z1uMl0+;WD31@I0Od0BpIo#t*>@uqIfR?dB9u~^^(qv%dtrJqY?Ob)N~$QCAaNiHa&!XOjdFjW^FHR$jSxUid9FgF}mH zs^D%};|7Ol34St5Xtok*uC~TS`2` zdHy96nRg_U#_l}uDm%}4seWl8$Z{UG;KrkzWloMU$oy z5BYc#5ZcAXv_(}S7zG-*20U&ge&kqRFzAKQPkQz?FFE{XShC$S5nv2r7d6xtn%d4j(yj7$-61A| zc3Zi~?@o=lMq=t&^J_|sG)~8jGUT=ZH~TGLxz$v-?Ms~5=c^jgJ>S!Bt6kFG`VeHB zAF3`wo5MpJbDDt&F=1bfHlN7gqAIXGAnY`MGS0$rL_P*jHP zTD?jEhQ9&tcBcUj-;wztKl2w-8)fskJho96>v+LUN!^@u4b&D(RjvhVtom}RZ zOX7k~vXN%We%hym^V5}M7Fw6C=Qyk9{lWf0Tq{NWT=RAx^GiD_cT|s{MTIse#eR`n z(kWpbS}X_*;*YfF4?4sKNs%zATW{?rz#@Z$OnCId8CNVd7JN6>{NenA&NKGBA6=kM z#0l)oiJSm;L;qU%%0^{&+lr-K(Y3nx-c2-S*y4D$v;D*gEgZGj0*Y!Nz$M3v$bQK) ztv%V9Y-w&YvXB&c$;<;W{C;SondsDVHFJ>RRg$1-y2k~|w=%c)u73=>L;dKQ>rUd8 zcx$)G?Yn1`5)!&MrM3H;GdnW7939N!=xs00e~gnmgfvW`sKe&3&M(nzlcD|*ImD;^ z7|OxM%roCJ_>YY+$z~`0r-~^&fcOArbpHuEqeE)PLiVA{2$eHYcT3|MP0FuKU}fVf z9uzdRCL*(zfL?F-Y;eyHwiPgZ^ZJM-5YnkvdY)21{?sMJisIb1RO>eQ0t$coDm>gV z;GRM#w8IAbE`;xpo+IaFXee>QtL>03C(n;ptmTm(>Xp!1(kN!pH-hve;liMp1x?80 zn6Ug!wi|HkM7fOL4>Efl=9XUi7!ir+WJ8(Nc5v<@Z6b+SD!^a-Zzgh9m<9^KxP*E_ zG{|Q-{^vET7vm=6UvAZp(zLqvS zjgu}WkxLK!G*efv^7ihuAgvzTtU~7vo>=2Y!d&!n#oZYj)JB?$OC6Lok;{60ynAqO zQO3Xaa>zoP;ANjzVxy?-X26#M^2~YAA!kxGUsq?~+;H6@b#Kta@Rn(ow|`gpktX*T zuRjVh6Bl7A|AXrL`XQC^IpO#ew88Y(uS%47?Rw53LL(&wjvlHdQupw4_I#T*arf!a8Atr`S;NkITUHZHMLnt2}S~)|uY6VSP0|c;C-?Z^8E`K10N<3XaV7%LISBqnsgKx^Hj;OIRwC^Q%@s0$)-e* z{tfuMlZfl9!vXM+r0-T>@Qe5+K>5p^+a=*Vlq__xg_K#}FCIwd#>S%08RGa&2Yi2l zSf`pVlh%tHqT!Uup25fV%HF8UY*%a6Dy(D7%q^oMqR9|?nTBQRsIS#6HHC)PED#%O z+%yfK?|qNZ)4wrBFmvmVv*yk9ji+lKqZd6B_BT7wP=J$Im!(+E(JTdN8cXBrrvybr zzE%V|@3bT=5AAN#MgT&;r3`#IM>g_ zM-wC~{3q8dKH*xwHO)92)!jZ5y+Uk4R4Oy{X#y>76AF5lekhY%A3W5|aOAy5`Nj{1 zUAJE_1LlJGSN(&lJ&m*sAR#w^n`$BUjLR$Y9&`6&od&|b4Z(_iF{ z$*@B(^Pd$dJEMebsDCU{F?kVeG9+PuT1~O>vr{=BlnTs+UpIbOSRsFl(RS<>M(ig6;H2Vj}OC?T%&CmhR7zcLB zfLVnaZkRi_17nXj45gXpx0@^Fd>ENk0*WMEg$R#gkxafT@+qKa0Zk&%1j;*L~xP~3-X?8*a7M5!Sosw(ML>!65uXU0fjzd8Apn&aSX;oaq^=D>H`JZLGlN?BDM!QXC0tvY_ zt?8wLBDZ%d*y8y80XH{%{#Q#IAlwn)a1|tJavn{w9nc4c)v|)itVML-^5{F~Vqf zv4G+aJxpR}sTI!f*SzJOI9ekwn-kNSy%6pDhZNrusRlOmghD z02%xX!pMa1J&<+>JHG`4WXP_AD(WO8phj@D89p)Aqil+<#{a&((gwcS!zlE?er|WiCzqUO6xB zDO12!d;d`ddOB{-{sq_AtqJ-+ zG3Jx!utxyluv$AC1H`|>8dFxfEO zo9$Foe3Oq*qPbx(QG+;S;{}RtUbuOTk>p@uAP_=ENY01|OPT~_b2IT$+)2yhe6{XF z&2QT?)Om3YJImdBoq-}l)dk89d&S{b+&s%)l+4vRd)^o~Cd6n-2>L4oweWA2 z+ac)w>ZB$zQ&154O+e0?k&3cm|CuK9xVg${$UDhbA+Mrl6qhhgLC`-h=W&-sQ3RO1 zR2OU1`cne1G7froqsee2=*Oh67z7F)B%C55!I~4`|1%Jem$ra8y&4q*Vxl3~3F!n= zUP*)4tZ`7-6WoR%q#9g2UUEXEL@)9+#K)g~mvlDJhkj{ps_zN|(5EoWYYe0Yq!hF` z0eZKUD%5tx;h~7kSA5E0KiQ2b>&Up$?>}k_t<#tEU7D&j z`}-v0)c?23Y#G1y@8e(9xI>JpJ;`66Gz=U>&`)_!te%TbtI^f`bVCubLQ_$?U^Z8B zWx!*_R@|%}rW=tLIEvrM$dMRoMhYnoSC{}pOz9Y4p+BuMWXPv^Ab0-cMAA&I5%k?g zq!yfQd_XPq5kCFjJ%9Hf`&~s`P@YJqdX;gnXEo{ zxjno^AB%oGuH{+B&5hwq1B5f?=;aQjvCY<)UZnpFYMlZ6F@I9y>diRX8M1<5mGMwL61{-ba!#cmOj>C8hX}*}?qJ#yfL72Mg3=;BH+ZvXE_jidqWn^_|l` zL#Pr?!0<5uBn&Ldd*MoqSr)b-&LiP(uyG|&BJu6GF#oAy>2aI#7K5O^!H){g8R{ti zs6f`>ic4j_&LBdUMF;@H-!<%k3IF9fO1<_|C2&5&0KLMd#c^Y&fwDyW{s zD;E5cU#SooisZTe#jVK5Lb-;;X^8uqin9X|<=4##vS?|^X8b%uXL`&6DhLypf(ay_ z`Hkg}-|0=kqhtMB?OQq(Q8MJ&lN}{*W9^)xhYhg3sfuK#vE53XK0boYnR^)kq!*fs z^4!L=Unf#bHFZy+J*$0BY>|{?B78p>Y_7ajO=(ldtRdX+lnK%NC_SpQxVp4zo{%(? z6TLd!{bec`HN+86s%f1ykU3LhTO;`jRWac#u3+>GjE?hXb!m^3%{q51ICeG1c0pu5 zI1+tYUV6tENNjvTF1rRI_pzI6S4iq4zPO72r1E(elrY8V) zrj+j8sqEF&!66!%V5^Hw8O{>QN2aaGO1|Td5YE}(fSHMjwXGc3P_{bFRDDjdDyjX( zB|m@+?u%IB$j4siH!A)U8~`*VfTVy0p9-=r0alHZAZSEDI8R#nfcutJ1a5qD(^!={ zK6q|66WH(uv@i?`K#$8z^3jj@mGG!v+FaUn>=%yI6Bg(MFC$qkeJEuVxuzZ*`T)Vc z4SZGA;JCSOLD3wcyEOnVxt(fLY*hK{Z9!wEvewCrXpCkt;zXHRI9P-1eIaLzn}6t)(uwKEz_Ss3bOJQ8_fYwJ}Jc={(gD zl_UrKi|qbYlIZ5`Kmz78cn!pS1;BEM01^2=z8%avLU=8&&z<(tNbC)9{&^2#+LxY_ zWv_zAl)d1P6EbVZ5l4NQ!?f{Z8le<8Z>73i*`Swr31Xo@e z;^G4IO-zdGhT}=rKplSRU<+5+?}|3M`=c|+xT!D1!fNN3^RAXAZHVz)bI_OlJT?CD zj`Dm?AjOUeVe2}vp7P@;OuygLC+T+Uw6Eg&*I$R3pur4e7Mswee|vL$od?rMir>{D ztkgEsu#VxgY<02>$X}o_LBzNa~2MO{mVgGJb+zCCG#X7cvAWpSB9KcH4QyI zaB2RCoYdel$EAQ{G$!eVrzpd*8MH9q-^ekxbFizB(o*ytTxOU?&z*v?)by@N2nDB{ zk~ukI+7E>M?2l%J8MltZ&7t&adX6AJg6Z+RY^zC%J|7r;K_PEOkfE|R-I`lk*{K=9%zOwf?Y zPIifn;^jtXdPy>j;6gI=z2-6RG;M`9oZ1cv+w`!erlZF&rkZD$t;?I-jkRF(%>?fs zkJ7)XPlLWfxa6*YeB$lgTVT`8e{8od*>eee1Q38J5V8Oo_HVcvcJhFxJD{Z*$YZtz zhAfi6r?T5f!OHEU-olxTn}ZfNf6Xkc0Yxqz3~~N5uFgGQ*nOC%2~bY?^7e-frV8`( zB#ZP+)1L?Tr)5)JF%>VP=HG!pCQTv&v;oe*-G6Qc-acEc6c;?_5wIG2gunoT1Tbp{ zpo56d5QBGt>e-c{)@iK&XH(COo!c@&@p6NqN}PK|SX@cub17{w{Wj$~2AmwdaF*x% zJ74>UAXfI9SZv9uT`Jg=OEzSFA4vkQ4}fnngXP;wn8?XKx0F4sHH2OpjK&()j7sgY zpO;Sz&7G0SnYjJ#OEl2&T|=pPCeVuXT1ml8y4kIeXE)x{IacS5e=%med#t~ItUq9Y z&CWb}=2-N`CGp{s@!aN3tL_GgxQ5g8t)k@N`6Q9=vD70V{xG`!zK_qOBm07P#AdTP z0b{=L7JmRe2>k&5{wGEMH+wiMkq1c^YAIFKcGHBo1|7>6VAVFi?tPKS%6>tIC#~UVLmEV; zxbW-YFMwnWW?ZnJ!<&=@R2hccwpwsQhNOEYR)v^i5%lz{#l({O{%N@G*nRN{ z)ob)fC}F4NNXf?(HrFWO@HCOzl5bn$!y0P6wDsd|?2GDNTD?e9Zp~cF2dM00jJ)fJ zY(N%^)Yz}(_K8;UsyJT}h2NQzMG2ivc4Hh-K(MiosxUOjy+g)dPT-{v`Dg$16!#!; zPpLF?gx-0n*sw4%GQ=)7z0|-i@N!Rq@k(rXH(#q@iD8t;BwAsA$aBOOStGzUK6xq(nL(7*lKk zR4x;+<8nT{SAyE`kpJZEPtRUf{{JIwXCi?^B=F4fCVc`sHHAKd;b|4kKRp~m8-3O0^CKG?OOF-AiD`B4QpyTe zepg$i`J(0Ki+>F~f-8VE=xamzK2}0Q%NQG@5E3yw)8o2nbdwEKGJ*;tSb7sVsH?+Mz~B*VV>T^ACKpx5+F{scrM+ zJ#29Fan5+W+_rpwnqf*{(2)41Tz7D4^0}?u-V7#VP#nQd{^sDjMxSG#&*OxX#|gOJ zyjTng9)oTMVEFUxaH8KUU1A#SxH^~@1Jf%EZ82)5o{V0=wC`teb8}znG)(hU%T4wN z)b4=3XKyB}LOpyO?*elcOOd6W$vCwDMg3OOY;H@`O!p95o~J_}T$Vx1r5KRATdYp% z=XfAa*Ukt_PgYJNH)dx~yZ7e9q4shv9Oob6VaSZ{-etnZ zLAnc;ySfek^bv|t|5xVmlu|;Y~nv9s6AHGzn*m0@`$6tPHsB%I&^G5qzP5<%i1H zYouK{l2qg9dHn3MQ*IibCplR+QZEs_bg&S)4wb%yGSpWWF-9~t@Rx?v$^CgU)z>n& zhQ3}jc7!U<8ar!CvCsxPDKq@%SLej6Uw_GWFJXi7)wuRUCroA6$c6=<`4SbK-i|xz z$f%72WsXT&v(AVTAI+KXQ{U&vc?DQmrsw|oJ*5S2cKWv}jP$oZOzu+*0RGexi$MfI zkCkk4t2M}vTll|*&K599X1YmRkKrM)+(uECUaSL+6xZMeuYMlgInSLFN zyhi{}PaC-5Yg_u>z!aHC8bKkG#~+%PIg*Hov|un88)*7-@#XjxWXMqd1`2^c`!gvc z__W^7_`B~6)5#M3Lh$iU11R=G*cT3eUV!^6iEIwU5o>n&xS}TnC4-y6(}vP%g>QFf z6p+n?{cT9dN$W9GVou|5XB~a&vzOMIX!3?2?i%1*13V1B67%Z3p+xZqx^=7)*PK|(z534AR~h2!f{hIHBJ4wUOEI<1vATJ~e+ zaBCZ3oVKLTxj$8^Qos8n8{lG@;fmQSDqn^;-4vIMF@3Y>O8u;39|x0Zsc7gONoMkW zv0)st_Re?G{ffc@dto+AJTrM=Fh$F!z$O8Z_dMwy{97ZdHbN$%R_y+qC8IYe&LZ@G zql@b}@Fxx%E17%qF*gzF3nNYVNrP)o(I7+*to=~_f`2u5j8Mhc-0<*8q1!i&Sgn3= z%oSE8Tk)X+*x~5D;FOzPw@Fm06ro@0*og@^TGu%LL}2uEM&mMJw*bV#1|UGuM`&=x zTvNDDy3jcUrgt3sfH>%AOhWdwVHvS_TMD8$SO@ z7U7$?I3@@+GoK6be|iij6^5(m#AaWqp0`5H)MKO<>PppYTotT%W1B{SU>+IEW+F9c zx&=P^FA$SVV8+9?0VLYZQ=#Ge>I7oJhN!~ur}JRJdzOfJ;7WU-rdxI zA>Hu$?sjn2bWwKJtc78HHa4+xd55od9T{7n`m8wgrJ%7NoYEy8>{HLy8 zpJ!y6ZfJJx9aqJx?p5r{(Tw>@UH?61XO`1WUc~UD?<@AW>C%+wIcw|rl#hqAYE^J& z%S+lWsSP{nO4TVLYTZ>m#do|7jcK!G?CHyn~t})`NGX7ADCd> zY&?C3b2r<0J7REPRe$aqneG1E;cd0_x93;IEAg==zI-&bQy<<#y$>$#7wgaGj9TxO zhpHv@*%jvx-p4Q>UV{fmTH5CJPfvgNYP*-`O+uUGI(rV*Qx9CaI+Wqs z-f=tpKiQ3cUkF=`eScg{=kb`n*d+@xRxkGs??M{1!T;W=4S#rq{TD||{>NhfE%x7H z|1I|4V*f4n-(vqQ_TPuJ|L$&A7v}$*j{Z%b{~nL>fB9*_|30k!SG}nU!<*~qd?s{h z0=Pf5^77&5_a%F(^VbqaIw$0;UTuka77<#6M0s{oy{O*kY>pRSzquwvd3mioJnMTj z*H`u1PCS3DgMTfNLQ{8WvLw0zc)EUjSv)p{px`5Rzkd4DPfa2&_y6gqf1O~-p8fw$ z_x_Rl-*p%M@3Q}w{r{2U|3(UYl=#25@PC*1|MK6**#86WC)xb@)p~V%!tpPY`rjSb z|B1XM|L?=<|37vU-G@8T%GH~DYS>(>fWytt)%qKu|lH2NHecG+ep?kJ>TK4~8{`aXeVE+D(gT?+|_W!c~KXU#*BSkdmWD4NK z{*U}%9RKx~{r_S2e?OJCUrA5D|7s`Y7g!#szM5C~t_fdsOKP|7x)NTSbk{wBhRwA$ zT=27Q@_+V>$l+Z^vdQn8Qg=-iE)~qHeqLSODRS9d`Q&l`t(#j^!UoRtX1yrr1}GRc z?}7+?6F2<+WldH?b^QZU_R^N+#hA5}uKyZqy}o@9IZckp;_-geiU ze+~QV=&G*!<`9?OVN(>P%vt)BQN32B+ve4c;AyYy7IZV^M)V@j4S z{qb3~)4MwkoKj?7!gY3j{PwD7r^|JBEu)lY1*KSyKmkqnEaVmf!pOsCn6+x-RV~e?AtgT-;Gg{YN%3 zmNvt=?>AXpjjBlfecFgT;Qzf-m3IOifBRte-$3)fcl zPzUah7|5=F-K9nu8PYZ~aS&~-X9M9qO1ydn`{MJ%mvy$qZDmB%lY5$7i|VimRqP~0 zZ9*)c3`FPcs%US2W={NqC$V~gtR*tizW1p4>e6(0d)7T~P%7_GugmPxvcqcqP4hyZ z>=edpE`)d++KaTe|o`l%tiBw{cGPDwW4by_4HQ)PwO*QlF|>`m)-w> z=Vk?*eR99{Z8gm%o6oG5zGgSalK;59aXT;D9^I0BvRPWg*ZzBZnn-H_Heay&#ItOp z^eBgzZ~w9TZDvz^x7k!pvvx0h(ccX1ZhPD{{(SfvnmO~{XYCj2{P~7_f6uku)7U*cVoRO-;7Pf_4S^Xdw$C=NVxs!RrPj!=_VWh zW}Y?PSiKUUJ7zQ|PFmM3{L#}Lm~iXy{boEme|xJt{DFrgPj7Cr zx7HTq^54H8|M#8x07uyWQMlB9UhMzH{$K3>#r|LH|Hb}a?El67U+n+ozxQnauMyhy z>V--9Ut{y@|A+YRZx2nvyS{z-H)GM zn{e;C@dyo_{j-Zzu0Nkv>(Kav56|sddrart4$<5W@!Sr{+z#o?4xTf&gFCl_H@Aa7 zw?iX=>^7fn^j`F&rg?w=|j&hLuLawCIae$S@d%yAvxyq({T zW0y2Q^EFTn$xGSsq-LZ8p<_jA0(A-phJVHw0 zc+c;3Wzv}D_glXN(_mHNw3pI^&%?{f9G0V%>O;qyZpfe8tGT^O3KVsjQgi!Om&H{%tsKVvOe)f$ zUYz){DprrSCkb?AL#NultTqSDO*Iv_={^-PXk1sf#Y?-E>_|+(%&KwGo&IFk`eMbP ze?1cGX+5KZRa0XR+TB%VXU1??j&n2PkB;kgbtB<=F7f zBt%!`BkSAWkknm1y1B|%m-d}rdc~s|lZ#ffZ21}%%Ohiu=2#ar!r z&3v;N-)!1j4``QaJoklWY#S3$n{od&fA?R9kLGs6zIpWW_V#*qM@~V*6mD1hj2`w+ z$gWp*Vk<*T%wC1P_V#+kQc7RFB5B-imNQ#Lfi_&5L)z@7)#yuO^;!z)?8W8P8hY&d zQNg5MR<_^DFrsAiFZ;=|oS}|hUxQqwiPT#{bqKryPFG}E{%V0-%=m3Dl9t?mHp`@dw4y= zsjz4{IMv9i!v!=k%X{0~|Arx*-D6A-8e2OEk3=BEisGw61?wruuUiyqsx zzuG}w4z|4C1L?P{EnCfcQR!&ge|Da2L#rpZm1pck)wXdPBVK2yL2&U`1%eT1kRwMt{yXxlc{lTtb z^HpKfPTu$hXS~j7J8NxGTG~o3tt%^|Vf|My39Gkl)UAC>Bhc?`b#2>ce`Tpx+U9cx zuI++Sw`|SI_(|cA((aWu2g6yk+Y|`5dUJb=rD-{9SZ?ibM8!_3pX_zb?X4B`X=zs7 z=i=t$$9eT3N06U$XVesvV~YR8&BEsm*Womj5#yVcWE`-aq$im9;rnS}PRE zF3|sx5@H@*RF^N1^CmfBf9?&6;x@Z@bzI-Fx=@9Al;Kg`jYHjhf%eDM-R*JrQF2Ii zd(tRuB>vI8-yYi){=sowiSS-OGVpje0aHTJ<{s@oPP)hJFHtIYGmFg)Brevc+`#$` zKt&Z@SU=iCO=sSq)m}$E^^qVxi>|$%rt$XQ<_@#Iv$(!)ZgPEpf2`@=zJ$A)c?Rr0sdVk*|=f<|jzF2n`Wi?Ir#h?Fhty+mY@#jrU_32GH3c&A% zX1~b_1Eu%dY|qo#9IVoubhe&qrlUxvLg|)I)cZYe|1oI0IlQGk^JM?U_GFg>PuZLk zwEwc6T~!AzKJN%Ce+Re=oIz+Z5%sr2lfP|rLq7ZA{ik*#O3nS?$N)=!O~ttDEXkCJQ2igL8HH4(?XHVXhU@1rZj zMmS7-v%5<>-Q}s{Q9Ux1GLFBE0ik)F+q;{~dt)opw*nq0SIyu&qTayz-Q4L^dDFia z<%3@xeQ>djOTZu9}*`8c1GeHx7k6ZN86Pq*W>ukUVcHSi-{)pD@yMg(BG zk+^Eeelaz_ym~XM{g3yg8}IG#^!oZYimJ}+_WTw*_sEWn)sa0h#EfjJBq*&2)!-F5A{mczQ$R_U9cRqvxMAe=)V^ z_cwWd_xg3V<0x$|ws-&2je!)qCao6wRO(|-C%yU)cbC7dTA%UFfi`e@UvRL)diw^4 zTI%)hf7V}Sr6DvQZdG0Xrhj_nJ#7t3;dcK=>-vv3)qRiJ9{+yu4$tpYif&c?a^D-P z&9^JVc|X+V+p8DfX9`0fZbBKj>|%Np~7x%%w#Rky4TeZ3a! zz3cJ7V*aN4+mze>%$%_?yv^{jz4QX%cBABif8EXYNLK}u0ifS7jt4eh@3uFaZ|UE+ z*IAOt=}Z%@8SIWr*=K&-nTcWA%7z4`^Ppd-_g4czCB549o)Z) zoOJt@elqIq%vyu){cqYF5vEf4zr2gG1|J)01`Pv2t_=uDBl0=bK!QyLqf} zpEd5Ya(&vxT#LCDb1mjt!nK5J3D*+tk@Rn|w|A1}`?dC6 z+PJiFY2(tyrH!k7K^wOlBkd8|@qLujYv(I!=Pc2@F$&swwB!3cpLTxGjt=eoe^{fW zor#Q-?5ABoyMT59?E=ZZ=w9h#EuZvlMhYah8g9E1Z0^5DUL0!a-MfQ!TZe~?r{2?g&H}5oyrW5`o*vw5bGe7^-^!`9k?$M19?V)Jp}ov% zEd&%HdXQz1B%OW$@sU>7op{7LC$zEIE69rR}WVSKO( zeD;lxE#b3IeP7>4JG8EkuJf_ed~7t|w;o3~v2JO9&uHFqG$2V5S>N7i7mTl^W<9)! zG$f6OsPT|B9>T`s{X9%=e~(S?sjnPzcC^Rl^tq3ZsPs|I6dUIcL(4sV?&)(+A2sJY zwuTgUv3^fI+j=27Grr&c4eV#aTFptJq=NAH!{~>Men`8J(GO`G3cwWL$iL%x5GZ$w z{JA~0pe9C>i<)zBSlu9+quSNW%D^4;nD>n7`)|I(8deM(+m@ICf1B}nH}AYLgJ-;V zA1c-xO})+L8JjmkG`hb2mpR6LbpzMlZ5z%WYu61^>+zd4+PZ(ly<=z2{mu7ymX2T2 zAIs@pb4GnU;+FT0nr|@AaGJR>`-D3LL9<^1eWR{3I!KUa7Bm~QL4vqQ5RQN25ABd7 zW`>X^F1y{0G^(^if3moQVmK~>KeUUIW~5`A%4_au-=qKD_IuUz^qMcV z?B&i{LD8JG(8;iwZxqe_;vk&0-g>Jvbmz{ZU2FTV?Z1uvmv{3JrFOg7fNl0hnY1d6 z1Tk89EY@vXE!4JmBfCDax4(~pMJ=D@_%px7v3s{ff3Oq)fA2kjdk^r-N4EGTKsaMu zxQ`z>!}sNyi|y#*_PAKFE>^7T#_hYgmSe}?2{d>F8iw!c51;2+zNhZB{odaDvIpI; zeLpMUVW+>}e4q6@^!i%&i^uP5_q?5X>Atr&FShF?4x0Bc-{ADtOrrVVPjByS=HD5u z`NM$(w-j1 z)cqsB2}A6(seUKZJp1)$@3LgSbnm35bx?Wd9!_d&74@`2VkwRK65nk!y!!5Hlly(_ z;XRC|@LR1EpW9Q>dYZd`kGcIfTB?2i=38v7D!}IUXG!ipXH!q^xsgu~t zlCbdny3DJvOu>l5+>gW5_lpu1YDS47szv{&e<_moytulq9zNy}?dtp?rR%71S$<>4 z)(;=Me?RX(+OLy@+~B;*owSJRFpHufEOIX>;vx)dH_JS)bknR#qavxIG)#-UO!6em zVkhR+9!@oh9@hAoZjvPjY@C@RA0d65Qr8q{e}P7e{S za?q^IFLIYJNEjNQN#?EvtXscwN~f-)e<)%plzv?KOiIQp1a;vjURIUU4+zte_2R^7 z%0f!Kx@5rW*e@UjF<`z^M5UXjP=O*=pIfl9GC!%1)=3_wp+EV6By+<&h@7}gvmi|? zj|Ej}oSf3RT`)QO2|^T4`@_?!y* zMV-cZkR*Om6bWOW7FDd7ENVcyw8w zIxLzf@SG~nGH}BxiI|hLPTk1|f23(zWJK)}o@EtP zRm6hh+j2H`lq8{F1^M(4#kK2KMV=y-3O|KXQF~PHcWalK42v?UiXe6nR({1A4Qf~x z5+F$)cy*LoOfur3sOvKGGL{5`Q^bDCG7OV6Px-$Dc(iUMf#RB1$f6()e=00g_6hK; z6PzFhF%0r7sp~k;;vfo~fMLtL3Y(~OOTY9gr^>xFbCSYM%Cais)Xi$gtG%?&Sz+!8 zk9HhBp~%AAtNb!Z>MSg4x6Gr;M^Ygf)2z-A&SC0cIb;#CEa!4oC0-RdNa+(EUF9VL zLDR>9$4D^bei8+FmASQ(f5%RSQboft3RzN=DT<-0c)XM5WzCY!PZ@BpU;wkicQOuH zL^_mSmSzM-vzV==Rm7~bpMy9@uH+IYb?l)m^03U}I6Fn<#W}}wg>`^r4bU+Of+Li8 z_scR&f;^AY*w0aNLB$@5SP*$$Ngh<-3@qmQghv++doIa}h>r}|f7gY}YDbNyA+oCG zlbLlFsa~N2*sWQD5K7asAP66_Z=F*Vy@PyV`06l9orD{M8iyzyl$h_J7NaoIBf=u9 z&g}9)q!`)S9?WYpSsv>c~&Q0h&INc$ZIq)R%H=NKs&L|f1YEz#ix9ASU6Ex6no@Pab8+Vza^fZ)fj;x_Dso325y?4^3`!%@dz}l7qiWXaa4%0 zr0}x<3&g2P>^GJn-&|KAmIynZ{f*Vb;xA}&%2(&;Eewd5O;usHd+4shak2twlCY|) zEJl|5X%g2_f00IRl_Pgj6g6TXU=f_~=u9K(R6&}@P_kXGj)S_ab6KJ}0u>3L$e53v z97)OvkEqbn%aH=~!Lv9;kQQ+c+=d|&#i+1A;u=FH$axQ+|EV+V#{@9WkIOPIvk+s@ ziKD1?@LG6gddj1#i1Y`Zkmj=PODENgfj}1S^`YiY}NUh!7Uuu#}dV$W^W^B5-b5Wa3Dt_52H9rSbY^11+NzyNmc~TDa$NzkQouHx5%)< zVk}p@f3zeBkRuo)*eN)sUWT}KLO(){V9CT#%CPXuD$K)E_I1U|!Y#&>#OcI^32F`- z#>8jcMIn1PC5{`h!aaPP9NF$yA>zEK@+8J$JIPe2aUxJqb?OvpD8~q!3=bdlylP<>TTy*ghq`Xd23ADf19gT8$U)*I9r`T#~Th zX1L)sDijptB$2|7@*~8tgmi`_=0=$1xT9>X82lmkunHJrKQANX7+c!snI#I9mo41t ze}qSej)O$b01Q&Ty$oZl;~eLxF48!ZYRs`6oER@XshtQZ8YNYY72{~#vnfw_G`gTf zJJu)P^7{sJ+=te~Yk|G!#;FMFhOzrX_nmKFK1LypB;#u&vR{nSor$ zjzD(R;Hn}2_5tVMT&Gxs8CVLUjP`*9Pk71-FBwQDmH0|~D-Bo{UL`Lc>nw z*HFly+&m_R*Qw+nuo6!)kT3v4%x<(~#*ky2pp%23VBf@*gDuEEK$IOxe=gLBA28Qp zm|#D+PQeJAWShm{rg0e;C~Sa6JRlG%sJjtPXHq6SBEeV#^YF0G0<1$|)ChPN{3yxL zAt%}4IIaO;aEjOGVgH4|d0+_0PSF1>7FZd?$TkNcmp5dCfvF>VQF1unr})=V$pXR* zo@zHkzM;B7~To z`1v7(9X!L32w~wfGi)vf3Jdm>uO{9Abd_*dZtCD%)IRzfiv_h@A+hAjmY^4ilDc+S zexS`67`40+_ar+UG~9Ku%4Be2n|XMTc};vEEmG7cP%U~B&j>59u2=|YSR(6*Tg8ZY zfC9#wkU$#W5;a!95phdDohGPw4jXeRD6S}AytY$q;7hWH1l!qJehn1orpNZ01S#vVBt&+K zNI{ZO*ahgFd@u~I7{gcwU%^7ESy>>XM$V=ze*_#NUJu<);MYNI1G=Oq!Z-vzh&2&I zN%9nIpW#J5SA_NgVG9ILQ>BF&oNTjlqcC{A$l62loG+s!2j7@iI)%+ zhKGUqNs$|z0xysf$1uSoE7S7(HfVugsYitxK4f2;u@@B~eP*@g!oRteTIf03P(#>pw03%yj~ z#fWhO&d;vJJScp8YfudS4$la%4BHUZSWuYb->wrl-ho)D-d9Tr3F;U{3)>0C}8uf5OCI z@2K!R^st~%Zy6pYYb*gt@h}?$@T2e~+l+pw4;RA&MI|m;kzwRwsukEJax0H9@TYRUG*J7tBlZ3yD!$Wlqsl1DJlK$1X)B1jPB@TJ%g@$kVQ%DH0G;1LoS z!rv=T+1G9%<`Jeaq05B&M#wNCfAhrd1CN~qjV+LL7mkMU@8<>43g83+ah@xK^%PS9 z)l^qGf-z)TKc2H>bLMhs-9JjMG!PKLN}g8xDm zj&!Cgr!$6#6>CMok%59j4FLiZ;Fs)dwzGrgLap-1X-1SV5@1D~WWqV%f674p_!?4` z#88PQf=1y-<6A)sNhBfB(HOz{GCv7Vky%+Gz#=aBI)b*4jKK97M;KEqZVX@-UkA)P z7ekGZaV{5#3HF7z!^oH>CE^05QY{d#@ZGSPk$0$B3UlQk&e}Q9x(HOEC)u6EqVcSd zhG|j>n8~LHFLw!b!GkFse~e?ulv1Ne!icyabgojNbwD7nJfb3mesPsSW}um1@us*G z*)$o8Xl=^SON{3{20I+rlZuxpHlnS9#}kA>id04IAPrrlDl`Txm>B2q6gSMvVUihJ zAV!VU=qN#0VuZmi0Ma86_bQAmw^aDW$ zAeop4;KCrfq7V*9fYjpEiSJ_n5>qWLo=}Rc!q}vWN#6f!JHX!VSgt!aQRiVkki+Kn7tUg8=|DVPY{q_=!A~h&-Gw zLWYnv(YwX;6CnJuI}kI3JW5eTWe>!1Z2=RBh{5T_hVTi%;4HDC*`X!uDO~fIk|aqz z{e;{vYXx|qqGA+*GN)z0r-Vpv+)j212qFF)RzxY`NeBcqf0NN7@`TbTHA?H%i%TkB z-&ESxaIh2;K>fiiVdjtuP6a)aErz^cze`Z?S7byQiT&upo&zQX1;iS6Ey&C;ysOj! z>XE_5k{}9)u!sH!Rm2f{l>Hm94~S0jhj0S%DLh!B32B2i_1`xZMX_+;17R4(f@AgO zcsFby0_u@ge~$>wQzIiGaQdl4+E?OT{8{nD6nTmLke`%zNZ@Pe@07VX)Nh>)73}tv* z+UOK@4zH0tERZ!4bqJ%LXfU2EQAFG)c%iV*!TV$AxF}}`Uj)_gw8g4Je(t<4z6nz- ze-{pXh<%7}iUW+)keFxb1GH3Pso^0a{c`Ae@PtdI7Gsj=@U%{^NFE@7h>yo>ithxC z5Cavde?#;uk;8|Y$7c1=7BMj*Amm6%0D{Gt#hv#ppq~QmPvl*(3Ii=Fwjo2Di}D1H#a;xoE|95!*6DPz0|G~$ zGL8hhP3aEkLCkxx99(GQrPw`eZ+KL&8Nm$k_DUHx5rP9zn(n+WE;SmuCa;7IhGhf& ze-oyfh}Oa{9YT&UKIB3XfUQL1AOR2C5_1DH1|x0S&?1bhHH7^k*pw0QvET~gE-+^t za6}k7F@*gj&&B9uLeVvFjm2?g=BG*Nr9Kcri`c*v$q_gq$eGLo1Rnsc=))l1aNtl@ zCgOl-tx``$7I=rVGaxC53m`rE6H5*2e*o@Yji4Z%ge{EE!bU97g^--kri8wU+TgY+ zwFRjKGj{rYC9H4{0UfZD6*wS=18WW9JRmmIVgkb$Po%U!k75}?VoTt~5o{->6B#;r z|9#0qsPUwDLkvu!*z$sa4X`GV#ECQ~Vp&8mSougk3|_=41btS9L&S617DKM$e_f?A z^|6QocrH+m5$}TPgpJ_b!S%v!LVAj&9czpd?Ix_NWTZhDNxW>#FaFn+s6kx#cxn}L zI1D0XGKsl@r;hO@)d<-EBLr-jcx~oSuZJinf5t^ng0(~Ee!N=k$s>G&4;SI~Cwn=zvlK4QK_F~M3FO342XTxB%oJfPex5MjU~- zEqj#&9~2e_p~%BX_Cygbq$?{kbcnftlOeh={jW2}cpe22l`C#;U`oW4vkvd2Kcukva0s5KUO# zAPYhpS98xSZ4;>W4N;{!2A2@-^H-;%>|$cJF6Dk1-BbSIt&Q( z;TMahj0z`+=4(eof4Y<3k7Y4!5*nrfD-^>w&9LBDPGFfNudtNi1hVpQA24je07RT6 zfP(X{uqn|tR^DiQQ3atfX2=C#Z6Lu34k`NsO~)Q)ljTGm$+$z9i5_M`RmdWdQLAYS zC>AM3o@b0~QGgWz3Rwd>p$i$rBpb1+;R)omtZqOVs8@g)e~@zkF`Zoj@40*(I!8olzag!J@FfTwFAX{7{xR=Ob z^d`WY@*0)N7-3=(I+RR@tES*Q$(!Wxof0Ag%V9IPCJYEYNO?B|KbcUFCxV{n38tKg zJJ27CypWv)f8$*%P1d4+QjQLYsf!)Xy}2*E4t5o*2ZbaGGAfxFC#AqRL^w=rKh_~Z zOu?~r<9QwZ)qvEHUxg;IgQ1SI#|g)fD!V7>aBmvw>{`Vwm zIbIoN7%H3)JKSG(JrPeHg5s`3*eYd{0V`ob!9EEk9~`nWV07~6VcBznieM!^g=c|;udj1un~Iz3mTg^Bnb!8 zimmAbD&hHI%%WULMyiz&Sp)rsI9(^@S)!*hrCsGvsFi*rR)quk6D1*b7@h~bSCS3* z@H9L;-jtxC|JB%yq3)TR7$L>!}tUk;;GpiII=K zCMXK4MyBB@a}7EG%0k+L2zu}kk%)@J@040%UiGB3aNz07*DhdKu zJ;I*DJp^h(L4jJa$&t_s!$UW(OcE;Q94r^0Wq2Nh0aOEE8f*{+2mk{MMMH<9-0-Rh{NSX5RiV^K z1Q45(_!CPNa0%yo+PH}rQ-Ulk0+*<44iZIbAcF7zB_a@bRU}L)%SJFb z{y_k;`#>1gA(p!sljBf1?Xiv9c5w6aAR?*7)HZ`*Nxyk+!DYi%1FW zSS}J%;LVsjFsETCLAFy8x&XEurv~mVGfW8r$Y3ZA;VUp6NOqiFMR=(ofg%8brQ*-S z;}hNux{hKCDgnfp!^j`h90VyOq!_2-ivyn)({q3dhtLkdie-)uQ>Q@QWa33ce+Lr* z5~<8!=yVVUNHTJKVL4FsG2Vm$N7JDi77>}ta!@ds6r?H*Ee;P3^+>h~Afl*6Y!<{g zIG(CNKm;du&&QUCZ4&db3ULKg4MKX05J0N(7okuLaIu<%UhrbEopHXk1;qwdMFj+T z5l@2v*rh5?fy9G6o~jZ81{m88e@mK}3DOG!B7&OLioyWOzzV>7RHz-twhX6N5t2Zx z145!WpRuV-0J+Piesfa+PW1i|0zL24Bmh;}WfjrbR_s7_$}RRO=tqL8(MU zUqlDNdLVxWLoQaPF)s|fjUOvZk7ObY8F;f6emnp)u`1;*#L8rbbS-jqe<4E=>`#eF zz)t`fq>8YA2~?8FUuv=X!81qehB$}-Fs0UOF};dll)$23 zr$)-LBwPZp6z7Tz7X%|bO$v@vq0~8IzqM$BfM+;8;;dq^Pw!-eFli_rAV&ei8iyr; z*jp1@4;6p_{|A<+Fo!WAs1Kl85Nu=Od#FBQD;A=Hlp>%MC0!tve+XWnlx67gzt|=y z2w&v^m=88Kf$CC$cXlLfP{H^9^aF@eL>Nx^O-LMeRUqWJ3W1=Y9}&sHizFug z3J8-(Kso{u zhWe9PMAjMdT%jt0e@d7@m9R4zEAkN7W=uc60nreZ2B}plf^S81M6xpcdK!_FjL($i zFTht}Q`Vqx3>KwP0I>AseFMZB`;*ii<;nm|iPVFgU@=5+f{_LYOM)Z0kS12B79s0a zxoyxnv1c;W1n5Jhs!lQmBHZUdK8XcnLPZua{1iiigA$`6e^Jd!mvWRqnd2Y}?_f&s zz!@x}xwsCD8Db=LEduoL;)oalGfT}2WepU-Wn{>00Aj_4W4(k1_P`A%=Yb%YAXq9G zAqA&b5d%tq4}NKL7|4VKL#1&`yG{o_RA!0ALdcQLr7$Sp z&dpiq8Vv!ExI1Jlf}*OxLnL92`3({yTiL3nQ-#8(HVk0AGKR$01D;^22@64FiDZPz zegN;}-UuibQVKl-%L%Z1+Nu!O2mMVlH`cO70Pz4~e;+D+Trpz2pWNi8Vk46T0ihx{ zD1buL5Vb=KjYi5SNC{A=NH;2>6`=_Ts4MF-4w|$na>hX3GsTf%g~EJ=IDvH#TMQ8@ zP~ZW?Gz|G#^(TB4`Y4rb7AoUM#XaEthJv)2U6oH0mW%fRcibz<7b#ftSPb0(?iB zpfNECSgv4b3PUO0tH`#+7t}Zbz9`G=XPj)pe}^#EsF8#r2jU8u8TpJ$fOD%-EQG3* zk(3$wYne^&WNbldOOZClx*^!7?@$FQBSm3yk`SbdE}p2SgoTOLurpPc0$MC7w1tHm zE4xK3T*|Wf=rit7!_HN$njGkXtVs%Kq`u;Lx$F&s{?H5%)v9okSVRz1O~(;0Fhu9D ze^5e6Jv1EVBc7yBfgvJcjESQF9tl>J03xvZNc zXks!dK8=zf)JC9`dL4nBS;n!D>offYn463+5vo+`T;(+!#dxtWAW-8q5Ed5*E4zb0 zUq#@9l8kuDxN-yq>XR6l7{ZERWFpd;e~>A0L;`$7@-Z%Ql>x)VA*mN91}?q4c7!rE zLa2Ry3guPBsgy|w!-uy3lVYw!qKCE!T8c7+e4L5(1^E*-uL>y0O>_aS8gr4KM0r(} zmI}(SsvKb62J#uPL>LQSCEygmAkC3LhB*9yR~T2&^Vq$DWKLqy19(e7f{a3Ae^j{^ zXegqTz^f0%XvfF^6~ut`R9wjs5lXs&DZms+PN9V=4(pN|5aH!QMsp1W4FH)5hbNc> zF{~8j0;5SbISR#CA>_4!<)Kne$rFL`!MbGyXDlEASxS(R9SWsRC?wK^Wt}TQn{thU zPhdC*VT+{PPQhe}-on2HIRGdke{zQzM92n_4Hl`?FPYXdS#T%8)UbZ25P^(N?BR4fZ;Pe~L4kI^2EqUtT z^|^v?;CJHdk&9i*1;d|V7)g0jzz$mi0^+n~g$0A`;cKx!pgM_t0z;C~ARY+M18S

gS!CKiuoxE0(u6CDWs=yFQyxz?%=>H;gBn=Vn{;-3*cx> zZ$Z^FAlsbEOVU!rmqQih6~)LvRw`gfNE^l~>eLJ%xF#AMSw|cynVtjaDI|_T=OPfM zomeUrlT{vztwU5+xq8%bguf{7g~%N#vbD;#pqo_OX4*dIe>0g!a4UH9p1=-(b|6F8 zSFu1RHX&YEr79(Otth@gH<>$p*pH|C`Z*qd;w5{P-Rg7;dz-- z99!})Eoxgse|C1o?IngrU=~1~MTV~|GOAct5N8-&GBHq|5X=Ba3fb0xvb73s#}>#w ztub$aAgH*cIsjVA0FFxS^6{FX^Cqq#l#qTy{uQ}1L~vBh8MC3DmQpIgs$!$^1h_wC zN@BS&NQ%@|j3ZV>CPB#ic14S3E3(mMmR;4H!)b4_w zidlte#|k$hmN12I3z5%&|6$S8m4$0SX=y+~BAcX+ilTw1#GWB|szNMmIYtbp0iX@H zl_^G8Da)cVWG4$uHWX$_p;Xa#G0>=dj*G$OP#gsJtk81iu~ZpX!B%RGP}4S2EfgYj zBpgiRe;^d20V@lXi5-dAP{@o&ns`b9mRE}v!d8QzgBbBd&mvWXpdbuLoWs~+h+O4Z zfUqDj#2-!+pg>lmd$9e;>LMdV8Ec+0f{6^`aiD-=5oPh@yptl|3PHfI0$nGDiYI~f zuAKT@WoqCNxhiK*QZmsZ;BxpyaF=RPm{{IefAtn{uu3W-T`*HY&5*7_f5nI+yBOz? zeGclPL<({3Wabhaz#GlPpr_8*bcmKH2PlY~TLJu{WaAnYV)|j8V-JEbL(fq~)fDC8 zihT!94APMb`iJ$j83(#WUWXDhl^1HlBnRwi#EDGrpT_@<|dGr3&mTpSfHR2g10p$)R*L#4H%$-O$ZU{s1n zj1mMx5F-da8;Mk<@D@?pMSq8FfhUf3GR6C_81ZpHfQSS@ADtE=Smn7g<_Y9D2$0MZ zPu|lHrP5r15tM2TR2HcaA%HXXh!_k)e~DuY);s(g4M?S8BFY)XgH<^;)y8H|iqTps zwMl5ZtMAWh>FA$XhiDLoR zB`Dg}LV8reCCnj~Wo%)i5LCn1QB<6f$3FK=C5zZtE|u^m6`=*@5!2O?NlYd@<}TbE z0DpB09YSOr(Gp3Crz#{sZID7m)Cp{8p(+yUAUyT|5|wffmF zAdnAEr&t7dY4SD|@{2{XWQ{Xo@FvhPB!7um5J}`GRbrZYs>cP#1_wY~MSKMCZ}Bgn zI*G7S>&BT%k-)+9lzxGRw>aS;!@4D2MGzyVz%z_iKugfgobgK%-C_p>@8FFabFu3V&B> zxlM~f0Ba38lyGbe*|-e@Uey{{^4QXW@Lf!G@Nk9S*m$I%U=^3<$){j1C_~RArIkv5 z(TOX7+*P71#9Bp-z>_(O!BMerY;Dp?03?WqqPav9nBIaYG}H&frVZ?fhL0jHm5?Z} zhd3DpE6A#4P=S+6;V|gON`4RtF@K=8(+^O-5^-VdN+4+obV76{-v%8>RF!xH_H8PD z8M9o-<1rLrB~)drOwbhN1U#khl2Bu@fEglkVX!L;b8rG7 zTm&8*HHGR~Lxc^P5mop^ja8x~fVG+Mx;eaJS_~2@G4*2D8U&$zmEmQ7P$^V7g(`kU z4nC=Ln1=YZfHjyw!~j*3gnvM)6DB9IgW-c=n#jCWI(8{?BI1P4bCoJ6GAxLOrxXNC zEwX^1-Kk8H5Cq?XgijF`Sa6uWBzC0A*dpD72YNbVg;`cP2hw!d@uko?WU&hIk|D+J zp|C_)TF5ViE(H^Efji^|LMA0~0$+f+#|^>rQMn<)Bg${54wa&skbi|#%Umhi(>ny) z4;X{0laN>e-$W`SWZ_9MB`U&!mG7%?K`DG0_Z^ZtL9bAy6g(v+S$I;u$rcM$F0&#? zrXn-AfLaUk3CGT4D!d?v9|paG)3x|}AkL}s zuz|Du+!WCv7>4G6L4Sp%3vw)getH43iwNVZT7*IwnJi~Wzl8O1{z{dJ$DUJcmROa- z_^{+m>KT%e_+DU9bpgmFH6VESl}NLqng+o@%s8A=Xh%YS z%P6)GA_}^bbu2_j6?{lYMdYH!Va;Hvd$K=qqZ~zNBpE~1K7VJtsBJ5U&f*ZC%)~UX zy?_d|p$Kw`MS#c_^$S^w6{^D;bO01W62J;=k48eyg8o>Luw;mVf&fdSj(BsP0Pi?Z zUGYMhRj!KJ5ljgTG$QZW5n-ACue6Bi2NVL6PzGXJ<7Htj7pB+&CSs*}H6R!4Wi(%E zN(I0u4^>DdQGfWewqX6L5_+L_R)0 z6b?vH>iiebW{_x22{~>=WRcT^YqQA6Uot?Zzd&h@%8oinOb4AX^C;&FB=$ zc!Ft)^v}f-!EnQ1WcS09%X|xHI94eS68~^?H2!GKT+tCs7Kn6#xFJyV$wGpcN>rJk zFy{yXSAPW}`dP3YK~xA=Vlsh3Wh&ehH8B$9*ooVxIwHjT@LypYGuaq4Dy+}>T*SJG z#Z5mTQ@xT*j7KqhAj_zFAjCQnlJI|&my8I+Hi)%=aET$QBqAMA`VO$dg3^j5t|F9~ zDuALD5|LtYkC+M4Mj>#6p!llR3Qz?~;VO~V8jDgf(=%(-a&VD%m+pc+W_?mARMXwEf!0n(kWQ9TG!JX4R4Vphc{BV z%Ft&u)fKU9Y7k=3AVo#2$5tgI5U4JSvVTB_pNL zP77rRIKt=9`H~EwD&c{as)n2?a(`pvL9fv3Z*5EEA7C16XU8#SWii#tpm2U&UiZdf@qfm)_GF*; z1)5ASNRc|CZizX>24Xho3RJ#G#a95>skfn$a!O8cR6x&y2q)oL%A~-iX0L+l5e{*5 zIt5_^mOsQ1)i91!@sA*tuY@m^3(rNlwaG>3sEI#4k%2$TZ~16$EYIfc~fRn*^>YvkxWBRu6iL#1`aT@O@b7uWUfdU6lTZVgC#vZ zrAiMJW-G@9-v@${Z+!VX=*%&j33r zz=L5=lqXObLB#}uLVqWDQVj|FeR@w~1N!3p;z%H)u)B1iETUYe-6#YqdMv67)CoM6 zBU(vM5~vLnirk8KOBImm7%G^D#g(Y00&0^SS6oKc5qUoH7GUWqjDTr|Q4Giow?+X) zEQiWMhEP?2zzJhVEesuc%Z_6~I65XXQ++|KN%+AyL$x_pF@KimEmevTf0xoQ244p~mCDP71_9QN!_OxmoK*9GG$R6ZTx7eD9Sz+mm+1AUB?hAh>PbA7umTG* zQN=P9=Yc7y3S78>u4*q~Y*U>GKCtS^1LojedpZYkx0+ckCMG%Jg`6{SH*_EYvO)Ay zUyBSoodAyf!hc%XZ&bx1AVlbi7-p4*wM8fn$*2RciDP66=<$GB35eL_R0d4M&|;NT zt`gD+1fWNWW-G---A4N@6rUtfQFkg~0EweM0B6W1P}(KZhbnHlLh5o$W1Vi4s}8fV&T$e|(cM?wYxpNBCM(<$ z(|<&K2ZV=`uV4sJ>;?s8QOZPPfh9%kLu?$JCzU-_^*X#(-d-mKsCEG+fTtoXl|t0= zaeS2<*nMBBB?I+w3=wNn2Re{w7IOx$N>jq#hIps5*b8$cY*U0V5kvz4ScEFaqLfJ@ zA5JAYT}5pmvg5-mI}*4UX}~%yMB^cwP=6qnqIGb;5EVKA0Y%_iREs0t2c)1(e)-TW zUb4+p_E=t!%B>*uIqF#15@e zp?heLQT$l3P?RnJMQFqAW0SItl;9d`qE%B+8N3+ep$rkc0>D0`iddFp^RhOS>&WWT z5%M}PV>qpYQw~Ge3B4v6a&l@ zTv+}ss{#TP28&8Jk}e~56P71~!$WMy`42sIS3f|N*=7E;k^uCD363VP1*97wzM1TBOoT^F% zIEb^e`@tS9%57otfs@L3(s8^BysJ=kWn#{dRLF=>Fo->lZJeWd5#OOATsSl;$U3dG zb<{oC;E{-b_`@QKDwYZ94oU&3`mjco?W|xInFOK{DU%PZlM=6qh1EsihdnAbg^DOL zK03clhpyzI3D%mErJxRqU4NG;F9Luf728(H%%MdQ48jYDOB5d1ibo`hdaH|R5k$EGZ)(djY~iRtOk(IRBVOh1ZfC+Nyllz z4fYjd6Khut*TnEKEI>3>!xnBr7OhrHA}c`&g~(j&^g`w;;ke42>3;}w4+k34h2$BS z+l?bQA?(cQf5;b=fC4Ypfj~O9L>7ta%pqk}78h8NX)RR5ELE0}SQ3Ro@qBPab2YbAi|as>B`p7J$CCL9D7msz6Vzq(aBO zN18wa6{<#u9dBw`s(;=*Rt?6La$aB-D96Aevpp0Q7lxFhTHzqE9HYo&Gro#=1v)&` zRbe~T63->fiDL#j7r4~XO*SkypnOJ^b1#X(5haPl^&-?%DwUWhGQk_@%fu0gT@ud< zbs#SMJ)M7Rii=qAKr#G@`?xwx7tAA336I#E$b*3u>r715^na&PQs+GbWrRxph%U?@hrZ8-J{Gz z7N{fgAhM6#W)&D_qXh!E5>+;cS@k&EY;q7;N|we(0Mc|?R6s2<34J=iBUZt59OGP3 zA*g4HjLNP{m4CzoF$A~;ttK=X);I$Q_H1E7Mh2x;)F)T9Onh}!Sj9He^1(fdRYD)s z-B*;00uOj93QEA7R~=i6Wva{wEf1_LkTPZLVp)=8p?qfyb?^Www?xPf5GjUKsoLSb z3a^R~Y!McMEllZ;Oo>@i`XYE2I&)hkFhK_x@l<&mp?_+Ruq5yVsOlwNI^pVaTIo|a zHV`8VF0=~2;%pMUq#7D-l7OoM^PL!hCc=S34R529%P1U9H1dyP(!97)P8ebS`>(v%j#u;Sv42B|9*HW(sft1p@wPGNK zTjm%KJAc-xz8pEA;5%VuCIeoTMGZ)ujE#EIIX4vA%*2bKtQNscSYU7jl$}bo3P3YL zFN6cJI>>cmhp?JNL9qy`FpHp}B*EyoMWQy;dW6w{5>}-u0-CH;VH6+$2vsCDTrEB+ z(qZ>7KrJF?&>1pOrlHAzlZqh8*Fyj=6y;`nL4UgB-oUUd1)Mqxh$&RSGGppvFSMZa zhJun*-%ZDjvc*ivSe*vLtEwuJsbnpp57~0K7nu$+2o!dqO0LBL8G-7uM5;}NV2KQ) znnE%vCr3F}Up|f_Zc!&hvzJN0=zv(o*x5`(Gz9agqLYr87Bq&9ik!tlqE5A|<9YFN zYa75ByHN*TVkO`w`bq@!W&czvlVIq}=6~RJU!g%#M6lZ-&r@uWOdUa_77>HUB$6m> zB_eS^RHG7f$ANj+76gkFQpeK~GZ~E3oF^VAmf)IVgBF&ZIkrl~Wo13s8k`lIehOPLKj8g@{gSbfshj$D&pX_EV^U3JwK>pc=2z0*o|>H`FUwqkpNX z_eL=i@@)ypM>@r}QVB-bHNmupqcaon%y}R96DoJioB}U_oD1LFFV2%uAbO&*!bM_|$*rZ91Goo%i2L}Cjs zvsP-FDiRZ00#@;K)Dz|qUbBw3qJLIZqSM)Rb`BX7ID?>a_$Syp;Eq6xnFR%(lSVKe zAg53TuTXMsuF#RAqk$ux7^^DX5GwJYFn)1}NM{XYHSqzi#VG^IRTcq5nP~Dfsm&6J z_=AQ)<+7qeX=t5P!4AaFtZR`d0x=`RGO{pRDB!EZt%&rMI+F@&k?4OAF@G>oQD+d9 z@CPxJ*p9x;Mw|)9)C3B|^U5sr0q_^Dm|RCHZX_~Dh;ZoiRgA?#I!HD}u0%ybGXVNv zRIm{Pm2gKeT7;-k_d4{OY#t;jg{e5`z!!mCdRVm!#JPrV3V{X{%~k*vp)^gcirolw z3oxEtLktFUQ>ze@u~0P|ihrD|hz3?-rrd`}mCsl+xsJkvk%eTJ6;fNIV{<@;SOFaN zf_{pHPqecF0skm%YtNUD)*&xm3tGMwto{${1n+>WnNF zpg=I4y+txgta|l2A_WE>XmhT^LIJ=s0ad%%aDT6E*z_pcEGN&3hqK;NEAr~aHg&f3qhH_vP3YODGH8xg+)QIgbk;0|}gMV8128@x^(Y{xtNGBIi>i|a(69T@8C^xB2M*|ueRqj?rDp?v} zcNim(Ab?|ZoDVsaj$-s;=(5^y18^eQ=ankDJNgDCCQqBO_zXZ^Y&|S9KsHPTSBK&N z8SA82OfiBpB911iZ_lEvMTNy{dMn5XB!v(pP88fClal-lxDSPF9Cmri5Z7rUe6#@wYb?6zg-M z289b_JH<^+J_eM@R^r2Ozw6&Y?Pu{!XCh{LGT0nhZNI<4qJ|oVaf5E58F(tyvR#f3pN8ekuR<-fM$z^kHqmFHL&w*p%1b@Su#3mpEA&9 zh&d^fZhw{(hUnhwrHF3qbVKkQ_FMPj$!z+?U=EA|?4yr5GDAyHwrV;FH%!3dM1i^l zyTGa+5B}@d7Hk~o@FybCx#1DGQmzJvV3?Z>YI;=tqvamTZGh0Dx;xcNr~@p^b-G2L zjjnb#`yOnJzGCR+Wb-lf6m!oSE%Tt9mCfLOn13HrGR$_(<}uYb_yFesfRDT{ zNq3RmRBfX9vMk%ZV!L@6Sw;`n7E2^Y&=gvYACxtZFn~1uIM=f(UVJUETowa#2^(Tk z4Sz`{vml70RPyaD@@!QLy!(m%V!J}2Z(Q0I)r#=Uob?|uB7=~dqKi9IVek?myvV!g z->&h2;A{NI9!x*T45279S_b}-O$}RL3D1_@{C9X#;7V8ZNw(@Ld;or7(+w6Hi~wx8 z`1=~L$aKSwz~r!5K+8?HhMpTEOVtl-R)6MuQo>=}Sf#+bt|zA_uCa!=+3A8yYe)}7 zaS~yO&KPooWNLh=%3NZHVbk<8hi%XLtlVG|!^F;nX~|<4H7Dr(4*x*d4^9`-WH&li z5bV8+crx*6_$eOdJ-Md8#$4imp=B6vcf10?GMpgL9g^M`X7aU6lFQ z{_&z9#lvjOV~3uag@dNivXp0~FFqFxcU&WLFF{Gk+rpjCTaV ze49QL`3B=64Mt&2z%zlTsNh&SC*5~Ve$OUsNi-7rVnc&J-y>Fn9qu*S4D;X}+cU))sXup#<=RtPzt$Sj(O&OfdHMx+htz7p0T=KM173RZtZT)*+ap zH`Ulkwvg3;u+We^%_+_nHW?*|Ezrb1_%|?PC^VkJ;}Wl=lML#i!v^cq4SLigm*`gl zCM;_ag0Wh%U{u+Bse+X)I=`8(3W?3ikrPZP%NN07y&;!6SOO=1 zpLFPaBLX7NV?OdxmHFfXVcXC_UTPDm#82iydeDQU8t`^+QsLdY;&py}xWTg>@P>>s zyH(mqBaVUmprMUDBvGyrA2`AmXr5@-zCXpgLDbL{~KQOJ^DB55oU59wh!unV(@ zt-kdj8_GgJuW#(8KK=#^n}F36G3DET+Bnn+I`^yO!RwUbXbP*03-XPrwv20sgE*|) zhX}3)o(OG_a8fOMyr~|i$OW4L6M9(YmzYHD%$Ni4eTRe2VfSKu(1gOoV{TveG<|6x_8!uz(;^^|XI~_xi5H zc9W!4J_R>e+~^Fvj#6P7tPpz6=7e|e$CW0EBw97n5Gs6mPB*0d#k7d!2&s5iwMN!j zR7$u>X0@j{xSBWIDB0EUJMjgE*}STxT*1Qw!Tx&>h#59wnsCA=Dj%1_N6VW6gAiyU zgk?$*Y^-cCnHhqiF`p(rR|@y)=p%23!-3h>EVn66#6 zRu&6<8X`+IxMwz67z^-sFDSfQXf>Nc9Ay`VDczJH#bBXAp{}93W*Ih45u%zh8)cfd zKa2_Ihie9#{eqnN)*69{AW;>Prmo1wkD07`)0T*hgat>2w#l-PjK*evj!7v0(+&3L zCqoeoGJLXL^kzLPBvP0gXau%3bgVj`VnRZ%dp1Q9mKxk(4h^CfvUpRCt(wO|)$^li zVF@B5OdOHTs+G_VB#$}lIfqI(%<5ReV7GN+z+`Z`LC0sbaLm7nst`lfXYK~{={n0D z#Cqz4U@T|bU#$QGqlp%OlP*8X<$VWy_>k^io}W2=4_Qehp-i~brT3^NI4K_z8#0eP ze}3hQzTExvue;NyPtX7O)0g3f7?*$f?bp}8onJpb{ET(r>!;U0{_^?x<@NFmO07?) zm(yzu%9~q1e|+KF-u?ddx6kJc_bsmwwFYh~wI?w|?CF zvGikQY61Xr6g}I2jx@I}ENQlHOmv#pIN8TZNweLHQ0SXXD87DO@0gD-0Vlcp~CB9;!hVVYup9_AUujT71p*mE8y>o@_! zWANuatpR&3X${zOO>4lOkF*Btg^3TC%REk&vSFqUMD+qDy@TCD)H&-o{IfB`rEbRJvdqH9w=Gt)Y1Ed zhPpp+q#~4~;~JRSjx`lwfv}}b|1{q06M2jep86|)Fx#p64OFdd&>*&{+I75B`W;5F zpVV=}Vvg5AjGE(Q87Fz1Kp56I*+Qs4TKr^(vH1(Oj)u3xUBl`##y^|eD^d+YtJ zwF6oEFt++H~q3VUk1L6$2*uZ(6EDl z8N{^q=4*ex_U7wo-r<{q+6Hz9k#=ULAkKbWFc!1@3eIA7dOJ$XY{WL<)Hj}j?`W-g zC-w)wuyGe6LZj@Ca^o*Vp2lFrhy<*_vj(e-Rsw4p)G-5r0RcWxqz#LIwvPCP@oG#> zbRcjeIg{HBf7Qk1x902#O2^qvPiQx%0yp+Q_0R_a3oVAhS%MM_1>4uMX{9!?f?n0ebxoJ?qgRa)aH;iK zJuY)zFK4__+t~X?jDfh>_yQMrV+>p+jWZArgEh$FA1na^-hb%?-c9fF-M@eL 0) { // run tests const framework = Framework.getImplementation(); -framework.reporter.verbosity(Verbosity.normal); +framework.reporter.verbosity(Verbosity.more); const spec = framework.suite('Specification test suite for WebAssembly'); spec.testee('emulator [:8500]', new EmulatorSpecification(8500)); diff --git a/tests/latch/src/util/spec.util.ts b/tests/latch/src/util/spec.util.ts index 318e552ec..4529d73fa 100644 --- a/tests/latch/src/util/spec.util.ts +++ b/tests/latch/src/util/spec.util.ts @@ -82,7 +82,7 @@ function consume(input: string, cursor: number, regex: RegExp = / /d): number { } function shouldParseLine(input: string): boolean { - return input.includes('(assert_return') && !input.replace(/\s+/g, '').startsWith(';;'); + return input.includes('(assert_return') && input.includes('(invoke') && !input.replace(/\s+/g, '').startsWith(';;'); } export function parseAsserts(file: string): string[] { @@ -136,13 +136,19 @@ function parseHexFloat(input: string): number { } function parseInteger(hex: string, type: WASM.Integer): WasmInt { + const literal = hex.trim().replace(/_/g, ''); const bytes = type === WASM.Integer.u32 || type === WASM.Integer.i32 ? 4 : 8; - if (!hex.includes('0x')) { - const n: number = parseInt(hex); - return typeof n !== 'bigint' && isNaN(n) ? WasmInt.nan() : typeof n !== 'bigint' && n === Infinity ? WasmInt.infinity() : WasmInt.finite(BigInt(hex)); + if (!literal.includes('0x')) { + const n: number = parseInt(literal); + return typeof n !== 'bigint' && isNaN(n) ? WasmInt.nan() : typeof n !== 'bigint' && n === Infinity ? WasmInt.infinity() : WasmInt.finite(BigInt(literal)); + } + const mask = BigInt('0x80' + '00'.repeat(bytes - 1)); + const negative = literal.startsWith('-'); + const positive = literal.startsWith('+'); + let integer = BigInt(negative || positive ? literal.slice(1) : literal); + if (negative) { + integer = -integer; } - const mask = BigInt(parseInt('0x80' + '00'.repeat(bytes - 1), 16)); - let integer = BigInt(parseInt(hex, 16)); if (integer >= mask) { integer = integer - mask * 2n; } @@ -155,4 +161,4 @@ export function find(regex: RegExp, input: string) { return ''; } return match[1]; -} \ No newline at end of file +} From ef2a958c13bfbc9ec65587927f79dff09f817ab9 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 13:08:35 +0200 Subject: [PATCH 22/26] Remove old style properties --- tests/latch/src/debugger.test.ts | 3 +-- tests/latch/src/primitives.test.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/latch/src/debugger.test.ts b/tests/latch/src/debugger.test.ts index e17c9e1b3..dd4d97f8c 100644 --- a/tests/latch/src/debugger.test.ts +++ b/tests/latch/src/debugger.test.ts @@ -10,7 +10,7 @@ import { Framework, getValue, HybridScheduler, Kind, - Message, StyleType, + Message, Step, Suite, TestScenario, Breakpoint @@ -26,7 +26,6 @@ const EXAMPLES: string = `${__dirname}/../static/examples/`; */ const framework = Framework.getImplementation(); -framework.reporter.style(StyleType.github); const integration: Suite = framework.suite('Integration tests: Debugger'); // must be called first diff --git a/tests/latch/src/primitives.test.ts b/tests/latch/src/primitives.test.ts index 317ea8bf9..731a95dde 100644 --- a/tests/latch/src/primitives.test.ts +++ b/tests/latch/src/primitives.test.ts @@ -8,14 +8,13 @@ import { Message, TestScenario, WASM, - awaitBreakpoint, PureAction, StyleType, Suite, Assertable, assertable + awaitBreakpoint, PureAction, Suite, Assertable, assertable } from 'latch'; import * as mqtt from 'mqtt'; import Type = WASM.Type; import {Breakpoint} from "latch/dist/types/debug/Breakpoint"; const framework = Framework.getImplementation(); -framework.reporter.style(StyleType.github); // TODO disclaimer: file is currently disabled until latch supports AS compilation From d892cb58db0f8dc88567f42c0f0385fe6c12d1e1 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Mon, 31 Aug 2026 14:37:00 +0200 Subject: [PATCH 23/26] Fix debug test suite --- tests/latch/src/debugger.test.ts | 95 +++++++++++++++----------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/tests/latch/src/debugger.test.ts b/tests/latch/src/debugger.test.ts index dd4d97f8c..26ae179ab 100644 --- a/tests/latch/src/debugger.test.ts +++ b/tests/latch/src/debugger.test.ts @@ -3,17 +3,19 @@ import { ArduinoSpecification, Behaviour, + DebugProtocol, Description, EmulatorSpecification, Expectation, Expected, Framework, - getValue, HybridScheduler, + HybridScheduler, Kind, Message, - Step, Suite, + Request, Step, Suite, TestScenario, - Breakpoint + Breakpoint, + Verbosity } from 'latch'; export const EMULATOR: string = process.env.EMULATOR ?? `${require('os').homedir()}/Arduino/libraries/WARDuino/build-emu/wdcli`; @@ -33,7 +35,7 @@ integration.testee('emulator [:8500]', new EmulatorSpecification(8500)); //integration.testee('esp wrover', new ArduinoSpecification('/dev/ttyUSB0', 'esp32:esp32:esp32wrover'), new HybridScheduler(), {timeout: 0}); const expectDUMP: Expectation[] = [ - {'pc': {kind: 'description', value: Description.defined} as Expected}, + {'programCounter': {kind: 'description', value: Description.defined} as Expected}, { 'breakpoints': { kind: 'comparison', value: (state: Object, value: Array) => { @@ -41,22 +43,14 @@ const expectDUMP: Expectation[] = [ }, message: 'list of breakpoints should be empty' } as Expected> }, - {'callstack[0].sp': {kind: 'primitive', value: -1} as Expected}, - {'callstack[0].fp': {kind: 'primitive', value: -1} as Expected}]; + ]; const expectDUMPLocals: Expectation[] = [ - {'locals': {kind: 'description', value: Description.defined} as Expected}, - { - 'count': { - kind: 'comparison', value: (state: Object, value: number) => { - return value === getValue(state, 'locals').length; - }, message: 'count should equal length of locals array' - } as Expected - }]; + {'values': {kind: 'description', value: Description.defined} as Expected>}]; const DUMP: Step = { title: 'Send DUMP command', - instruction: {kind: Kind.Request, value: Message.dump}, + instruction: {kind: Kind.Request, value: Message.snapshot}, expected: expectDUMP }; @@ -160,23 +154,17 @@ integration.test({ // Test *dump full* command -const dumpFullTest: TestScenario = { - title: 'Test DUMPFull', +const snapshotTest: TestScenario = { + title: 'Test snapshot', program: `${EXAMPLES}blink.wast`, steps: [{ - title: 'Send DUMPFull command', - instruction: {kind: Kind.Request, value: Message.dumpAll}, - expected: expectDUMP.concat([{ - 'locals.count': { - kind: 'comparison', value: (state: Object, value: number) => { - return value === getValue(state, 'locals.locals').length; - }, message: 'locals.count should equal length of locals array' - } as Expected - }]) + title: 'Send snapshot command', + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: expectDUMP }] }; -integration.test(dumpFullTest); +integration.test(snapshotTest); // Test *run* command @@ -185,11 +173,11 @@ const running: Step[] = [DUMP, { instruction: {kind: Kind.Request, value: Message.run}, }, { title: 'CHECK: execution continues', - instruction: {kind: Kind.Request, value: Message.dump}, + instruction: {kind: Kind.Request, value: Message.snapshot}, expected: [{ - 'pc': {kind: 'description', value: Description.defined} as Expected + 'programCounter': {kind: 'description', value: Description.defined} as Expected }, { - 'pc': {kind: 'behaviour', value: Behaviour.changed} as Expected + 'programCounter': {kind: 'behaviour', value: Behaviour.changed} as Expected }] }]; @@ -218,17 +206,17 @@ const pauseTest: TestScenario = { instruction: {kind: Kind.Request, value: Message.pause}, }, { title: 'Send DUMP command', - instruction: {kind: Kind.Request, value: Message.dump}, + instruction: {kind: Kind.Request, value: Message.snapshot}, expected: [{ - 'pc': {kind: 'description', value: Description.defined} as Expected + 'programCounter': {kind: 'description', value: Description.defined} as Expected }] }, { title: 'CHECK: execution is stopped', - instruction: {kind: Kind.Request, value: Message.dump}, + instruction: {kind: Kind.Request, value: Message.snapshot}, expected: [{ - 'pc': {kind: 'description', value: Description.defined} as Expected + 'programCounter': {kind: 'description', value: Description.defined} as Expected }, { - 'pc': {kind: 'behaviour', value: Behaviour.unchanged} as Expected + 'programCounter': {kind: 'behaviour', value: Behaviour.unchanged} as Expected }] }] }; @@ -240,15 +228,15 @@ integration.test(pauseTest); function stepping(start: number, end: number): Step[] { return [{ title: 'Send DUMP command', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: start} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: start} as Expected}] }, { title: 'Send STEP command', instruction: {kind: Kind.Request, value: Message.step}, }, { title: 'CHECK: execution took one step', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: end} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: end} as Expected}] }]; } @@ -278,34 +266,40 @@ integration.test({ // Test *step over* command +const stepOverCall: Request = { + type: DebugProtocol.Command.COMMAND_STEP_OVER, + notification: DebugProtocol.NotificationType.NOTIFICATION_HIT_BREAKPOINT, + parser: DebugProtocol.HitBreakpoint.decode +}; + const stepOverTest: TestScenario = { title: 'Test STEP OVER', program: `${EXAMPLES}call.wast`, steps: [{ title: 'Send DUMP command', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: 167} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: 167} as Expected}] }, { title: 'Send STEP OVER command', - instruction: {kind: Kind.Request, value: Message.stepOver}, + instruction: {kind: Kind.Request, value: stepOverCall}, }, { title: 'CHECK: execution stepped over direct call', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: 169} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: 169} as Expected}] }, { title: 'Send STEP OVER command', instruction: {kind: Kind.Request, value: Message.stepOver} }, { title: 'CHECK: execution took one step', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: 171} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: 171} as Expected}] }, { title: 'Send STEP OVER command', - instruction: {kind: Kind.Request, value: Message.stepOver} + instruction: {kind: Kind.Request, value: stepOverCall} }, { title: 'CHECK: execution stepped over indirect call', - instruction: {kind: Kind.Request, value: Message.dump}, - expected: [{'pc': {kind: 'primitive', value: 174} as Expected}] + instruction: {kind: Kind.Request, value: Message.snapshot}, + expected: [{'programCounter': {kind: 'primitive', value: 174} as Expected}] }] } @@ -318,7 +312,7 @@ const dumpEventsTest: TestScenario = { program: `${EXAMPLES}button.wast`, steps: [{ title: 'CHECK: event queue', - instruction: {kind: Kind.Request, value: Message.dumpEvents}, + instruction: {kind: Kind.Request, value: Message.dumpAllEvents}, expected: [{ 'events': { kind: 'comparison', @@ -331,4 +325,5 @@ const dumpEventsTest: TestScenario = { integration.test(dumpEventsTest); +framework.reporter.verbosity(Verbosity.more); framework.run([integration]); From 8c57f488e9e8457a09d46ddb12cda7d9df4a37be Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 2 Sep 2026 11:12:04 +0200 Subject: [PATCH 24/26] Add missing encodings --- CMakeLists.txt | 2 + platforms/ESP-IDF/CMakeLists.txt | 1 + platforms/Zephyr/CMakeLists.txt | 1 + src/Debug/debugger.cpp | 653 +++++++++++++++++++++++++------ src/Debug/debugger.h | 38 +- src/Debug/nanopb_encoder.cpp | 36 ++ src/Debug/nanopb_encoder.h | 26 ++ src/WARDuino/CallbackHandler.cpp | 8 + src/WARDuino/CallbackHandler.h | 5 + 9 files changed, 651 insertions(+), 119 deletions(-) create mode 100644 src/Debug/nanopb_encoder.cpp create mode 100644 src/Debug/nanopb_encoder.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 89034c669..d5f5d48b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ if (BUILD_EMULATOR) src/Utils/macros.cpp src/Utils/sockets.cpp src/Debug/debugger.cpp + src/Debug/nanopb_encoder.cpp src/Edward/proxy.cpp src/Edward/proxy_supervisor.cpp src/Edward/RFC.cpp @@ -106,6 +107,7 @@ if (BUILD_UNITTEST) src/Utils/macros.cpp src/Utils/sockets.cpp src/Debug/debugger.cpp + src/Debug/nanopb_encoder.cpp src/Edward/proxy.cpp src/Edward/proxy_supervisor.cpp src/Edward/RFC.cpp diff --git a/platforms/ESP-IDF/CMakeLists.txt b/platforms/ESP-IDF/CMakeLists.txt index c0e251a01..6cefab744 100644 --- a/platforms/ESP-IDF/CMakeLists.txt +++ b/platforms/ESP-IDF/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCE_FILES ../../src/Debug/debugger.cpp + ../../src/Debug/nanopb_encoder.cpp ../../src/Debug/nanopb/debug.pb.c ../../src/Debug/nanopb/pb_common.c ../../src/Debug/nanopb/pb_decode.c diff --git a/platforms/Zephyr/CMakeLists.txt b/platforms/Zephyr/CMakeLists.txt index f47956678..2db9fa172 100644 --- a/platforms/Zephyr/CMakeLists.txt +++ b/platforms/Zephyr/CMakeLists.txt @@ -27,6 +27,7 @@ target_sources(app PRIVATE ../../src/Utils/macros.cpp ../../src/Utils/sockets.cpp ../../src/Debug/debugger.cpp + ../../src/Debug/nanopb_encoder.cpp ../../src/Debug/nanopb/debug.pb.c ../../src/Debug/nanopb/pb_common.c ../../src/Debug/nanopb/pb_decode.c diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 1f59169b4..0f33ec626 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -9,6 +9,7 @@ #include "../Utils//util.h" #include "../Utils/macros.h" #include "../WARDuino/CallbackHandler.h" +#include "nanopb_encoder.h" // Debugger @@ -19,7 +20,6 @@ Debugger::Debugger(Channel *duplex) { this->snapshotPolicy = SnapshotPolicy::none; this->checkpointInterval = 10; this->instructions_executed = 0; - this->instructions_since_full_snapshot = 0; this->fidx_called = {}; this->min_return_values = 0; this->checkpoint_state = nullptr; @@ -295,7 +295,7 @@ bool collectValues(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { return true; } -bool valueFromProto(const debug_Value &from, StackValue *to) { +bool applyValueUpdate(const debug_Value &from, StackValue *to) { switch (from.which_data) { case debug_Value_i32_bits_tag: to->value_type = I32; @@ -344,25 +344,269 @@ bool valueFromProto(const debug_Value &from, StackValue *to) { } } +using nanopb_encoder::ByteView; +using nanopb_encoder::Uint32View; + +struct ValueView { + const StackValue *values; + size_t size; + Global *const *globals; +}; + +struct EventRangeView { + size_t begin; + size_t size; +}; + +struct SnapshotView { + Module *module; + const Debugger *debugger; + const ExecutionContext *context; + const std::unordered_map, uint32_t, FNV1aVectorHash> + *overrides; +}; + +ValueView currentLocals(const ExecutionContext *context) { + if (context->csp < 0 || context->fp < 0) return {nullptr, 0, nullptr}; + + for (int index = context->csp; index >= 0; --index) { + const Block *block = context->callstack[index].block; + if (block == nullptr || block->block_type != 0 || + block->type == nullptr) { + continue; + } + + const size_t count = block->type->param_count + block->local_count; + if (count == 0 || + context->fp + static_cast(count) > context->sp + 1) { + return {nullptr, 0, nullptr}; + } + return {context->stack + context->fp, count, nullptr}; + } + + return {nullptr, 0, nullptr}; +} + +bool encodeValue(pb_ostream_t *stream, const pb_field_t *field, + const StackValue &source, const size_t index) { + debug_Value value = debug_Value_init_zero; + valueToProto(source, static_cast(index), &value); + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, debug_Value_fields, &value); +} + +bool encodeValueRange(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + for (size_t index = 0; index < view->size; ++index) { + const StackValue *value = view->globals == nullptr + ? &view->values[index] + : view->globals[index]->value; + if (!encodeValue(stream, field, *value, index)) return false; + } + return true; +} + bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *values = static_cast *>(*arg); - for (size_t index = 0; index < values->size(); ++index) { - debug_Value value = debug_Value_init_zero; - valueToProto((*values)[index], static_cast(index), &value); + ValueView view{values->data(), values->size(), nullptr}; + void *range = &view; + return encodeValueRange(stream, field, &range); +} + +bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *bytes = static_cast *>(*arg); + ByteView view{bytes->data(), bytes->size()}; + void *opaque = &view; + return nanopb_encoder::encode_bytes(stream, field, &opaque); +} + +bool encodeBreakpoints(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + for (uint8_t *breakpoint : view->debugger->breakpoints) { + const uint32_t address = toVirtualAddress(breakpoint, view->module); if (!pb_encode_tag_for_field(stream, field) || - !pb_encode_submessage(stream, debug_Value_fields, &value)) { + !pb_encode_varint(stream, address)) + return false; + } + return true; +} + +bool encodeFunctions(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + Module *module = view->module; + + for (uint32_t index = module->import_count; index < module->function_count; + ++index) { + const Block &source = module->functions[index]; + if (source.start_ptr == nullptr || source.end_ptr == nullptr || + source.end_ptr < source.start_ptr) { + continue; + } + + debug_Function function = debug_Function_init_zero; + function.function_index = source.fidx; + function.has_range = true; + function.range.start = toVirtualAddress(source.start_ptr, module); + function.range.end = toVirtualAddress(source.end_ptr, module); + ByteView instructions{ + source.start_ptr, + static_cast(source.end_ptr - source.start_ptr + 1)}; + function.instructions.funcs.encode = nanopb_encoder::encode_bytes; + function.instructions.arg = &instructions; + + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_Function_fields, &function)) { return false; } } return true; } -bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { - const auto *bytes = static_cast *>(*arg); - return pb_encode_tag_for_field(stream, field) && - pb_encode_string(stream, bytes->data(), bytes->size()); +bool encodeCallstack(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + const ExecutionContext *context = view->context; + + for (int index = 0; index <= context->csp; ++index) { + const Frame &frame = context->callstack[index]; + const Block *block = frame.block; + const uint32_t type = block == nullptr ? 0 : block->block_type; + debug_CallstackEntry entry = debug_CallstackEntry_init_zero; + entry.type = type; + entry.function_index = block != nullptr && type == 0 ? block->fidx : 0; + entry.stack_pointer = static_cast(frame.sp); + entry.frame_pointer = static_cast(frame.fp); + entry.return_address = + frame.ra_ptr == nullptr + ? 0 + : toVirtualAddress(frame.ra_ptr, view->module); + + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_CallstackEntry_fields, + &entry)) { + return false; + } + } + return true; +} + +bool encodeCallbackIndexes(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *callbacks = static_cast *>(*arg); + for (const Callback &callback : *callbacks) { + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_varint(stream, callback.table_index)) { + return false; + } + } + return true; +} + +bool encodeCallbacks(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *callbacks = + static_cast(*arg); + + for (const auto &[topic, entries] : *callbacks) { + debug_CallbackEntry entry = debug_CallbackEntry_init_zero; + ByteView topicBytes{reinterpret_cast(topic.data()), + topic.size()}; + + entry.topic.funcs.encode = nanopb_encoder::encode_bytes; + entry.topic.arg = &topicBytes; + entry.table_indexes.funcs.encode = encodeCallbackIndexes; + entry.table_indexes.arg = entries; + + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_CallbackEntry_fields, &entry)) { + return false; + } + } + return true; +} + +bool encodeEvents(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *range = static_cast(*arg); + + for (size_t index = 0; index < range->size; ++index) { + const Event *source = CallbackHandler::event_at(range->begin + index); + if (source == nullptr) return false; + + debug_Event event = debug_Event_init_zero; + ByteView topic{reinterpret_cast(source->topic.data()), + source->topic.size()}; + ByteView payload{ + reinterpret_cast(source->payload.data()), + source->payload.size()}; + event.topic.funcs.encode = nanopb_encoder::encode_bytes; + event.topic.arg = &topic; + event.payload.funcs.encode = nanopb_encoder::encode_bytes; + event.payload.arg = &payload; + + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_Event_fields, &event)) { + return false; + } + } + return true; +} + +bool encodeIOState(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *states = + static_cast *>(*arg); + for (const IOStateElement *source : *states) { + if (source == nullptr) continue; + debug_IOState state = debug_IOState_init_zero; + ByteView key{reinterpret_cast(source->key.data()), + source->key.size()}; + state.key.funcs.encode = nanopb_encoder::encode_bytes; + state.key.arg = &key; + state.output = source->output; + state.value = source->value; + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_IOState_fields, &state)) + return false; + } + return true; +} + +bool encodeOverrides(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + + for (const auto &[key, result] : *view->overrides) { + if (key.empty()) continue; + + const uint32_t functionIndex = key.back(); + if (functionIndex >= view->module->import_count || + view->module->functions[functionIndex].import_field == nullptr) { + continue; + } + + debug_Override override = debug_Override_init_zero; + const char *name = view->module->functions[functionIndex].import_field; + ByteView nameView{reinterpret_cast(name), + strlen(name)}; + Uint32View arguments{key.data(), key.size() - 1}; + override.primitive_name.funcs.encode = nanopb_encoder::encode_bytes; + override.primitive_name.arg = &nameView; + override.argument_words.funcs.encode = nanopb_encoder::encode_fixed32s; + override.argument_words.arg = &arguments; + override.result = result; + + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_submessage(stream, debug_Override_fields, &override)) { + return false; + } + } + return true; } } // namespace @@ -445,7 +689,14 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_DUMP: if (!requireEmpty()) break; pauseRuntime(m); - snapshot(m); + encodeSnapshot(m, + snapshotPc | snapshotBreakpoints | + snapshotCallstack | snapshotGlobals | + snapshotTable | snapshotBranchTable | + snapshotStack | snapshotCallbacks | + snapshotEvents | snapshotIO | snapshotOverrides | + snapshotHeap | snapshotLocals, + debug_NotificationType_NOTIFICATION_SNAPSHOT); break; case debug_Command_COMMAND_DUMP_LOCALS: if (!requireEmpty()) break; @@ -471,43 +722,51 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { if (!requireEmpty()) break; dumpCallbackmapping(); break; - case debug_Command_COMMAND_UPDATE_LOCAL: - case debug_Command_COMMAND_UPDATE_GLOBAL: - case debug_Command_COMMAND_UPDATE_STACK: { - debug_ValueUpdate update = debug_ValueUpdate_init_zero; - if (!decodePayload(message->payload, debug_ValueUpdate_fields, - &update) || - !update.has_value) { + case debug_Command_COMMAND_UPDATE_LOCAL: { + const auto update = update_value(message->payload); + ExecutionContext *context = m->warduino->execution_context; + if (!update || context->fp + static_cast(update->index) > + context->sp) { malformed(); break; } - StackValue *value = nullptr; - if (message->type == debug_Command_COMMAND_UPDATE_LOCAL) { - ExecutionContext *ectx = m->warduino->execution_context; - if (ectx->fp + static_cast(update.index) > ectx->sp) { - malformed(); - break; - } - value = &ectx->stack[ectx->fp + update.index]; - } else if (message->type == debug_Command_COMMAND_UPDATE_GLOBAL) { - if (update.index >= m->global_count) { - malformed(); - break; - } - value = m->globals[update.index]->value; - } else { - ExecutionContext *ectx = m->warduino->execution_context; - if (update.index > static_cast(ectx->sp)) { - malformed(); - break; - } - value = &ectx->stack[update.index]; + StackValue *value = + &context->stack[context->fp + update->index]; + if (!applyValueUpdate(update->value, value)) { + malformed(); + break; + } + sendOperationResult(message->type, true); + break; + } + case debug_Command_COMMAND_UPDATE_GLOBAL: { + const auto update = update_value(message->payload); + if (!update || update->index >= m->global_count) { + malformed(); + break; } - const bool success = valueFromProto(update.value, value); - if (!success) + StackValue *value = m->globals[update->index]->value; + if (!applyValueUpdate(update->value, value)) { malformed(); - else - sendOperationResult(message->type, true); + break; + } + sendOperationResult(message->type, true); + break; + } + case debug_Command_COMMAND_UPDATE_STACK: { + const auto update = update_value(message->payload); + ExecutionContext *context = m->warduino->execution_context; + if (!update || + update->index > static_cast(context->sp)) { + malformed(); + break; + } + StackValue *value = &context->stack[update->index]; + if (!applyValueUpdate(update->value, value)) { + malformed(); + break; + } + sendOperationResult(message->type, true); break; } case debug_Command_COMMAND_UPDATE_MODULE: { @@ -574,10 +833,13 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { debug_SnapshotPolicyConfig_init_zero; std::vector selectedState; setDecodeCallback(&config.selected_state, &selectedState); + SnapshotSelection selectedMask = 0; if (!decodePayload(message->payload, debug_SnapshotPolicyConfig_fields, &config) || config.policy > - debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING) { + debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING || + !parseSelection(selectedState.data(), selectedState.size(), + &selectedMask)) { malformed(); break; } @@ -633,15 +895,22 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { break; } case debug_Command_COMMAND_INSPECT: { - debug_Inspect inspectRequest = debug_Inspect_init_zero; - std::vector ignored; - setDecodeCallback(&inspectRequest.state, &ignored); + debug_Inspect request = debug_Inspect_init_zero; + std::vector selected; + setDecodeCallback(&request.state, &selected); if (!decodePayload(message->payload, debug_Inspect_fields, - &inspectRequest)) { + &request)) { malformed(); break; } - snapshot(m); + SnapshotSelection selection = 0; + if (!parseSelection(selected.data(), selected.size(), &selection)) { + malformed(); + break; + } + pauseRuntime(m); + encodeSnapshot(m, selection, + debug_NotificationType_NOTIFICATION_SNAPSHOT); break; } case debug_Command_COMMAND_LOAD_SNAPSHOT: { @@ -867,10 +1136,27 @@ void Debugger::handleInterruptBP(Module *m, uint8_t *interruptData) { debug("BP %" PRIu32 "!\n", virtualAddress); } +std::optional Debugger::update_value( + const std::vector &payload) const { + debug_ValueUpdate update = debug_ValueUpdate_init_zero; + if (!decodePayload(payload, debug_ValueUpdate_fields, &update) || + !update.has_value) { + return std::nullopt; + } + return update; +} + void Debugger::dump(Module *m, bool) const { snapshot(m); } -void Debugger::dumpStack(const Module *) const { +void Debugger::dumpStack(const Module *m) const { + const ExecutionContext *ectx = m->warduino->execution_context; + ValueView values{ectx->stack, + ectx->sp >= 0 ? static_cast(ectx->sp + 1) : 0, + nullptr}; + /* ValueView points directly at the execution stack. */ debug_Locals locals = debug_Locals_init_zero; + locals.values.funcs.encode = encodeValueRange; + locals.values.arg = &values; sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, debug_Locals_fields, &locals); } @@ -884,28 +1170,41 @@ void Debugger::dumpFunctions(Module *) const {} */ void Debugger::dumpCallstack(Module *) const {} -void Debugger::dumpLocals(const Module *) const { +void Debugger::dumpLocals(const Module *m) const { + ValueView values = currentLocals(m->warduino->execution_context); debug_Locals locals = debug_Locals_init_zero; + locals.values.funcs.encode = encodeValueRange; + locals.values.arg = &values; sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, debug_Locals_fields, &locals); } void Debugger::dumpEvents(long start, long size) const { - debug_EventsQueue events = debug_EventsQueue_init_zero; - events.total_count = static_cast(CallbackHandler::event_count()); - events.has_range = true; - events.range.start = start < 0 ? 0 : static_cast(start); - events.range.end = size < 0 - ? events.range.start - : events.range.start + static_cast(size); + const size_t total = CallbackHandler::event_count(); + const size_t first = + std::min(start < 0 ? size_t{0} : static_cast(start), total); + const size_t count = + size < 0 ? 0 : std::min(static_cast(size), total - first); + EventRangeView range{first, count}; + debug_EventsQueue queue = debug_EventsQueue_init_zero; + queue.total_count = static_cast(total); + queue.has_range = true; + queue.range.start = static_cast(first); + queue.range.end = static_cast(first + count); + queue.events.funcs.encode = encodeEvents; + queue.events.arg = ⦥ sendNotification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, - debug_EventsQueue_fields, &events); + debug_EventsQueue_fields, &queue); } void Debugger::dumpCallbackmapping() const { - debug_CallbackMapping callbacks = debug_CallbackMapping_init_zero; + const auto &callbacks = CallbackHandler::callback_map(); + debug_CallbackMapping mapping = debug_CallbackMapping_init_zero; + mapping.entries.funcs.encode = encodeCallbacks; + mapping.entries.arg = + const_cast(&callbacks); sendNotification(debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP, - debug_CallbackMapping_fields, &callbacks); + debug_CallbackMapping_fields, &mapping); } void Debugger::dumpHeapInfo(Module *) const {} @@ -1010,37 +1309,151 @@ void Debugger::notifyPushedEvent() const { bool Debugger::handlePushedEvent(char *) const { return false; } -void Debugger::snapshot(Module *m) const { +bool Debugger::parseSelection(const uint8_t *state, const size_t size, + SnapshotSelection *selection) { + *selection = 0; + for (size_t index = 0; index < size; ++index) { + if (state[index] < pcState || state[index] > heapState) return false; + *selection |= static_cast(1u << (state[index] - 1)); + } + return true; +} + +bool Debugger::encodeSnapshot(Module *m, const SnapshotSelection selection, + const debug_NotificationType notification) const { ExecutionContext *ectx = m->warduino->execution_context; + SnapshotView view{m, this, ectx, &overrides}; debug_Snapshot state = debug_Snapshot_init_zero; - state.program_counter = toVirtualAddress(ectx->pc_ptr, m); - state.heap_used = m->warduino->get_heap_used(); - switch (m->warduino->program_state) { - case WARDUINOrun: - state.state = debug_State_STATE_WARDUINO_RUN; - break; - case WARDUINOpause: - state.state = debug_State_STATE_WARDUINO_PAUSE; - break; - case WARDUINOstep: - state.state = debug_State_STATE_WARDUINO_STEP; - break; - case PROXYrun: - state.state = debug_State_STATE_PROXY_RUN; - break; - case PROXYhalt: - state.state = debug_State_STATE_PROXY_HALT; - break; - default: - state.state = debug_State_STATE_WARDUINO_PAUSE; - break; + std::vector ioState; + if (selection & snapshotPc) { + state.program_counter = toVirtualAddress(ectx->pc_ptr, m); + switch (m->warduino->program_state) { + case WARDUINOrun: + state.state = debug_State_STATE_WARDUINO_RUN; + break; + case WARDUINOstep: + state.state = debug_State_STATE_WARDUINO_STEP; + break; + case PROXYrun: + state.state = debug_State_STATE_PROXY_RUN; + break; + case PROXYhalt: + state.state = debug_State_STATE_PROXY_HALT; + break; + default: + state.state = debug_State_STATE_WARDUINO_PAUSE; + break; + } + } + if (selection & snapshotBreakpoints) { + state.breakpoints.funcs.encode = encodeBreakpoints; + state.breakpoints.arg = &view; + } + if (selection & snapshotFunctions) { + state.functions.funcs.encode = encodeFunctions; + state.functions.arg = &view; + } + if (selection & snapshotCallstack) { + state.callstack.funcs.encode = encodeCallstack; + state.callstack.arg = &view; + } + ValueView globals{nullptr, m->global_count, m->globals}; + if (selection & snapshotGlobals) { + state.globals.funcs.encode = encodeValueRange; + state.globals.arg = &globals; + } + ValueView stackValues{ectx->stack, + ectx->sp >= 0 ? static_cast(ectx->sp + 1) : 0, + nullptr}; + if (selection & snapshotStack) { + state.stack.funcs.encode = encodeValueRange; + state.stack.arg = &stackValues; } - sendNotification(debug_NotificationType_NOTIFICATION_SNAPSHOT, - debug_Snapshot_fields, &state); + ValueView locals = currentLocals(ectx); + if (selection & snapshotLocals) { + state.has_locals = true; + state.locals.values.funcs.encode = encodeValueRange; + state.locals.values.arg = &locals; + } + Uint32View table{m->table.entries, + m->table.entries == nullptr ? 0 : m->table.size}; + if (selection & snapshotTable) { + state.has_table = true; + state.table.initial = m->table.initial; + state.table.maximum = m->table.maximum; + state.table.entries.funcs.encode = nanopb_encoder::encode_varints; + state.table.entries.arg = &table; + } + const size_t memorySize = static_cast(m->memory.pages) * PAGE_SIZE; + ByteView memory{m->memory.bytes, + m->memory.bytes == nullptr ? 0 : memorySize}; + if (selection & snapshotMemory) { + state.has_memory = true; + state.memory.initial = m->memory.initial; + state.memory.maximum = m->memory.maximum; + state.memory.pages = m->memory.pages; + state.memory.bytes.funcs.encode = nanopb_encoder::encode_bytes; + state.memory.bytes.arg = &memory; + } + Uint32View branch{ectx->br_table, ectx->br_table == nullptr + ? size_t{0} + : static_cast(BR_TABLE_SIZE)}; + if (selection & snapshotBranchTable) { + state.branch_table.funcs.encode = nanopb_encoder::encode_varints; + state.branch_table.arg = &branch; + } + const auto &callbacks = CallbackHandler::callback_map(); + if (selection & snapshotCallbacks) { + state.has_callbacks = true; + state.callbacks.entries.funcs.encode = encodeCallbacks; + state.callbacks.entries.arg = + const_cast(&callbacks); + } + const size_t eventCount = CallbackHandler::event_count(); + EventRangeView events{0, eventCount}; + if (selection & snapshotEvents) { + state.has_queue = true; + state.queue.total_count = static_cast(eventCount); + state.queue.has_range = true; + state.queue.range.start = 0; + state.queue.range.end = static_cast(eventCount); + state.queue.events.funcs.encode = encodeEvents; + state.queue.events.arg = &events; + } + if (selection & snapshotIO) { + ioState = m->warduino->interpreter->get_io_state(m); + state.io.funcs.encode = encodeIOState; + state.io.arg = &ioState; + } + if (selection & snapshotOverrides) { + state.overrides.funcs.encode = encodeOverrides; + state.overrides.arg = &view; + } + if (selection & snapshotHeap) + state.heap_used = m->warduino->get_heap_used(); + const bool sent = + sendNotification(notification, debug_Snapshot_fields, &state); + for (IOStateElement *entry : ioState) delete entry; + return sent; } -void Debugger::inspect(Module *m, const uint16_t, const uint8_t *) const { - snapshot(m); +void Debugger::snapshot(Module *m) const { + constexpr SnapshotSelection complete = + snapshotPc | snapshotBreakpoints | snapshotCallstack | snapshotGlobals | + snapshotTable | snapshotMemory | snapshotBranchTable | snapshotStack | + snapshotCallbacks | snapshotEvents | snapshotIO | snapshotOverrides | + snapshotHeap | snapshotFunctions | snapshotLocals; + encodeSnapshot(m, complete, debug_NotificationType_NOTIFICATION_SNAPSHOT); +} + +void Debugger::inspect(Module *m, const uint16_t size, + const uint8_t *state) const { + SnapshotSelection selection = 0; + if (!parseSelection(state, size, &selection)) { + sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + return; + } + encodeSnapshot(m, selection, debug_NotificationType_NOTIFICATION_SNAPSHOT); } void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) { @@ -1073,7 +1486,6 @@ void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) { if (snapshotPolicy == SnapshotPolicy::checkpointing) { checkpointInterval = read_B32(data_ptr); instructions_executed = 0; - instructions_since_full_snapshot = 0; checkpoint(m, true); } printf("ack%x\n", interruptSetSnapshotPolicy); @@ -1098,9 +1510,11 @@ std::optional getPrimitiveBeingCalled(Module *m, uint8_t *pc_ptr) { void Debugger::handleSnapshotPolicy(Module *m) { if (snapshotPolicy == SnapshotPolicy::atEveryInstruction) { - debug("SNAPSHOT "); - snapshot(m); - debug("\n"); + SnapshotSelection selection = 0; + if (checkpoint_state != nullptr && + parseSelection(checkpoint_state, checkpoint_state_size, &selection)) + encodeSnapshot(m, selection, + debug_NotificationType_NOTIFICATION_SNAPSHOT); } else if (snapshotPolicy == SnapshotPolicy::checkpointing) { if (instructions_executed >= checkpointInterval || fidx_called) { if (min_return_values == 0) { @@ -1115,18 +1529,6 @@ void Debugger::handleSnapshotPolicy(Module *m) { } } - // When using tracing, optionally (if the interval is 0xffffffff no full - // snapshots will be taken) take full checkpoints every - // checkpointInterval instructions. - if (checkpoint_state != nullptr) { - if (checkpointInterval != UINT32_MAX && - instructions_since_full_snapshot >= checkpointInterval) { - checkpoint(m, true, true); - instructions_since_full_snapshot = 0; - } - instructions_since_full_snapshot++; - } - instructions_executed++; ExecutionContext *ectx = m->warduino->execution_context; @@ -1143,10 +1545,8 @@ void Debugger::handleSnapshotPolicy(Module *m) { } } -void Debugger::checkpoint(Module *, const bool force, const bool) { - if (instructions_executed == 0 && !force) { - return; - } +void Debugger::checkpoint(Module *m, const bool force) { + if (instructions_executed == 0 && !force) return; debug_Checkpoint notification = debug_Checkpoint_init_zero; notification.instruction_count = instructions_executed; @@ -1154,6 +1554,41 @@ void Debugger::checkpoint(Module *, const bool force, const bool) { notification.has_primitive_call = true; notification.primitive_function_index = *fidx_called; } + + SnapshotSelection selection = 0; + if (checkpoint_state != nullptr && + parseSelection(checkpoint_state, checkpoint_state_size, &selection) && + selection != 0) { + // Checkpoints only materialize the requested fields. Keep their views + // on this stack through nanopb sizing and encoding. + ExecutionContext *ectx = m->warduino->execution_context; + notification.has_snapshot = true; + if (selection & snapshotPc) { + notification.snapshot.program_counter = + toVirtualAddress(ectx->pc_ptr, m); + notification.snapshot.state = + m->warduino->program_state == WARDUINOrun + ? debug_State_STATE_WARDUINO_RUN + : debug_State_STATE_WARDUINO_PAUSE; + } + ValueView globals{nullptr, m->global_count, m->globals}; + /* Globals are read directly while nanopb encodes this checkpoint. */ + if (selection & snapshotGlobals) { + notification.snapshot.globals.funcs.encode = encodeValueRange; + notification.snapshot.globals.arg = &globals; + } + ValueView stack{ectx->stack, + ectx->sp >= 0 ? static_cast(ectx->sp + 1) : 0, + nullptr}; + /* Stack values are read directly while nanopb encodes this checkpoint. + */ + if (selection & snapshotStack) { + notification.snapshot.stack.funcs.encode = encodeValueRange; + notification.snapshot.stack.arg = &stack; + } + if (selection & snapshotHeap) + notification.snapshot.heap_used = m->warduino->get_heap_used(); + } sendNotification(debug_NotificationType_NOTIFICATION_CHECKPOINT, debug_Checkpoint_fields, ¬ification); instructions_executed = 0; @@ -1175,7 +1610,6 @@ void Debugger::freeState(Module *m, uint8_t *interruptData) { // Reset checkpointing counters, new checkpoints will have instructions // executed since this snapshot. - instructions_since_full_snapshot = 0; instructions_executed = 0; while (first_msg < endfm) { @@ -1692,7 +2126,6 @@ bool Debugger::handleUpdateStackValue(const Module *m, uint8_t *bytes) const { bool Debugger::reset(Module *m) { m->warduino->reset_module(m); instructions_executed = 0; - instructions_since_full_snapshot = 0; debug("Reset WARDuino.\n"); return true; } diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 3fe7945fb..0f9e48801 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -2,13 +2,10 @@ #include #include -#include #include #include #include -#include // std::queue #include -#include #include #include @@ -18,7 +15,6 @@ #include "../Utils/sockets.h" #include "nanopb/debug.pb.h" #include "nanopb/pb_decode.h" -#include "nanopb/pb_encode.h" struct Module; struct Block; @@ -63,6 +59,25 @@ enum ExecutionState { heapState = 0x0D, }; +using SnapshotSelection = uint16_t; +enum SnapshotSection : SnapshotSelection { + snapshotPc = 1u << 0, + snapshotBreakpoints = 1u << 1, + snapshotCallstack = 1u << 2, + snapshotGlobals = 1u << 3, + snapshotTable = 1u << 4, + snapshotMemory = 1u << 5, + snapshotBranchTable = 1u << 6, + snapshotStack = 1u << 7, + snapshotCallbacks = 1u << 8, + snapshotEvents = 1u << 9, + snapshotIO = 1u << 10, + snapshotOverrides = 1u << 11, + snapshotHeap = 1u << 12, + snapshotFunctions = 1u << 13, + snapshotLocals = 1u << 14 +}; + enum InterruptTypes { // Remote Debugging interruptRUN = 0x01, @@ -161,10 +176,8 @@ class Debugger { // Checkpointing SnapshotPolicy snapshotPolicy; - uint32_t checkpointInterval; // #instructions between checkpoints - uint32_t instructions_executed; // #instructions since last checkpoint - uint32_t instructions_since_full_snapshot; // #instructions since last full - // snapshot + uint32_t checkpointInterval; // #instructions between checkpoints + uint32_t instructions_executed; // #instructions since last checkpoint std::optional fidx_called; // The primitive that was executed uint32_t prim_args[8]; // The arguments of the executed prim uint32_t min_return_values; @@ -224,6 +237,10 @@ class Debugger { void inspect(Module *m, uint16_t sizeStateArray, const uint8_t *state) const; + bool encodeSnapshot(Module *m, SnapshotSelection selection, + debug_NotificationType notification) const; + static bool parseSelection(const uint8_t *state, size_t size, + SnapshotSelection *selection); //// Handle live code update @@ -237,6 +254,9 @@ class Debugger { bool handleUpdateStackValue(const Module *m, uint8_t *bytes) const; + std::optional update_value( + const std::vector &payload) const; + bool reset(Module *m); //// Handle mocking @@ -349,6 +369,6 @@ class Debugger { bool getMockForArgs(Module *m, uint32_t fidx, uint32_t &result); // Checkpointing - void checkpoint(Module *m, bool force = false, bool full = false); + void checkpoint(Module *m, bool force = false); inline SnapshotPolicy getSnapshotPolicy() { return snapshotPolicy; } }; diff --git a/src/Debug/nanopb_encoder.cpp b/src/Debug/nanopb_encoder.cpp new file mode 100644 index 000000000..6a98ea606 --- /dev/null +++ b/src/Debug/nanopb_encoder.cpp @@ -0,0 +1,36 @@ +#include "nanopb_encoder.h" + +namespace nanopb_encoder { + +bool encode_bytes(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + return view != nullptr && pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, view->data, view->size); +} + +bool encode_varints(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + if (view == nullptr) return false; + for (size_t index = 0; index < view->size; ++index) { + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_varint(stream, view->data[index])) + return false; + } + return true; +} + +bool encode_fixed32s(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + const auto *view = static_cast(*arg); + if (view == nullptr) return false; + for (size_t index = 0; index < view->size; ++index) { + if (!pb_encode_tag_for_field(stream, field) || + !pb_encode_fixed32(stream, &view->data[index])) + return false; + } + return true; +} + +} // namespace nanopb_encoder diff --git a/src/Debug/nanopb_encoder.h b/src/Debug/nanopb_encoder.h new file mode 100644 index 000000000..e6e538635 --- /dev/null +++ b/src/Debug/nanopb_encoder.h @@ -0,0 +1,26 @@ +#pragma once + +#include "nanopb/pb_encode.h" + +// Small, non-owning helpers for nanopb callback fields. The pointed-to data +// must remain stable for nanopb's sizing and output passes. +namespace nanopb_encoder { + +struct ByteView { + const uint8_t *data = nullptr; + size_t size = 0; +}; + +struct Uint32View { + const uint32_t *data = nullptr; + size_t size = 0; +}; + +bool encode_bytes(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg); +bool encode_varints(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg); +bool encode_fixed32s(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg); + +} // namespace nanopb_encoder diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index adce0a2ae..3ee13560b 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -144,6 +144,14 @@ std::deque::const_iterator CallbackHandler::event_end() { return CallbackHandler::events->cend(); } +const CallbackHandler::CallbackMap &CallbackHandler::callback_map() { + return *callbacks; +} + +const Event *CallbackHandler::event_at(const size_t index) { + return index < events->size() ? &(*events)[index] : nullptr; +} + void CallbackHandler::clear_callbacks() { CallbackHandler::callbacks->clear(); } std::string CallbackHandler::dump_callbacks() { diff --git a/src/WARDuino/CallbackHandler.h b/src/WARDuino/CallbackHandler.h index c9d40c937..d81b7256f 100644 --- a/src/WARDuino/CallbackHandler.h +++ b/src/WARDuino/CallbackHandler.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -33,6 +34,10 @@ class CallbackHandler { static size_t event_count(); static std::deque::const_iterator event_begin(); static std::deque::const_iterator event_end(); + using CallbackMap = + std::unordered_map *>; + static const CallbackMap &callback_map(); + static const Event *event_at(size_t index); static bool resolving_event; From fb73a3ed16627f8df2b7a0b2fac37e38baace293 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 2 Sep 2026 12:00:05 +0200 Subject: [PATCH 25/26] Consistent snake_case for functions --- src/Debug/debugger.cpp | 468 +++++++++++++++---------------- src/Debug/debugger.h | 112 ++++---- src/Edward/proxy.cpp | 2 +- src/Edward/proxy_supervisor.cpp | 6 +- src/Edward/proxy_supervisor.h | 2 +- src/Interpreter/instructions.cpp | 4 +- src/Interpreter/interpreter.cpp | 24 +- src/WARDuino/CallbackHandler.cpp | 2 +- src/WARDuino/WARDuino.cpp | 8 +- 9 files changed, 314 insertions(+), 314 deletions(-) diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 0f33ec626..6d7566caa 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -29,14 +29,14 @@ Debugger::Debugger(Channel *duplex) { // Public methods -void Debugger::setChannel(Channel *duplex) { +void Debugger::set_channel(Channel *duplex) { delete this->channel; this->channel = duplex; } namespace { -bool decodeFrameLength(const std::vector &bytes, size_t *headerSize, +bool decode_frame_length(const std::vector &bytes, size_t *headerSize, size_t *payloadSize) { if (bytes.size() < 2) return false; uint32_t value = 0; @@ -62,12 +62,12 @@ bool decodeFrameLength(const std::vector &bytes, size_t *headerSize, return false; } -bool isKnownCommand(const uint8_t type) { +bool is_known_command(const uint8_t type) { return type <= static_cast(debug_Command_COMMAND_REMOVE_OVERRIDE); } template -bool decodePayload(const std::vector &payload, +bool decode_payload(const std::vector &payload, const pb_msgdesc_t *fields, T *message) { pb_istream_t stream = pb_istream_from_buffer(payload.data(), payload.size()); @@ -76,24 +76,24 @@ bool decodePayload(const std::vector &payload, } // namespace -void Debugger::addDebugMessage(const size_t len, const uint8_t *buff) { +void Debugger::add_debug_message(const size_t len, const uint8_t *buff) { if (len == 0 || buff == nullptr) return; - parseDebugBuffer(len, buff); + parse_debug_buffer(len, buff); } -void Debugger::pushMessage(DebugMessage msg) { +void Debugger::push_message(DebugMessage msg) { warduino::lock_guard const lg(messageQueueMutex); debugMessages.emplace_back(std::move(msg)); freshMessages = !debugMessages.empty(); messageQueueConditionVariable.notify_one(); } -void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { +void Debugger::parse_debug_buffer(const size_t len, const uint8_t *buff) { pendingFrameBytes.insert(pendingFrameBytes.end(), buff, buff + len); while (!pendingFrameBytes.empty()) { - if (!isKnownCommand(pendingFrameBytes.front())) { + if (!is_known_command(pendingFrameBytes.front())) { pendingFrameBytes.clear(); - sendNotification( + send_notification( debug_NotificationType_NOTIFICATION_UNKNOWN_COMMAND); continue; } @@ -101,17 +101,17 @@ void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { size_t headerSize = 0; size_t payloadSize = 0; const bool completeLength = - decodeFrameLength(pendingFrameBytes, &headerSize, &payloadSize); + decode_frame_length(pendingFrameBytes, &headerSize, &payloadSize); if (!completeLength) { if (headerSize == SIZE_MAX || pendingFrameBytes.size() >= 6) { pendingFrameBytes.clear(); - sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + send_notification(debug_NotificationType_NOTIFICATION_MALFORMED); } return; } if (payloadSize > maxFramePayload) { pendingFrameBytes.clear(); - sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + send_notification(debug_NotificationType_NOTIFICATION_MALFORMED); return; } if (pendingFrameBytes.size() < headerSize + payloadSize) return; @@ -126,11 +126,11 @@ void Debugger::parseDebugBuffer(const size_t len, const uint8_t *buff) { pendingFrameBytes.begin(), pendingFrameBytes.begin() + static_cast(headerSize + payloadSize)); - pushMessage(std::move(message)); + push_message(std::move(message)); } } -std::optional Debugger::getDebugMessage() { +std::optional Debugger::get_debug_message() { warduino::lock_guard const lg(messageQueueMutex); if (debugMessages.empty()) { freshMessages = false; @@ -142,7 +142,7 @@ std::optional Debugger::getDebugMessage() { return message; } -bool Debugger::sendNotification(const debug_NotificationType type, +bool Debugger::send_notification(const debug_NotificationType type, const pb_msgdesc_t *fields, const void *payload) const { if (channel == nullptr) return false; @@ -172,33 +172,33 @@ bool Debugger::sendNotification(const debug_NotificationType type, static_cast(frame.size()); } -void Debugger::sendOperationResult(const debug_Command command, +void Debugger::send_operation_result(const debug_Command command, const bool success) const { debug_OperationResult result = debug_OperationResult_init_zero; result.command = command; result.success = success; - sendNotification(debug_NotificationType_NOTIFICATION_OPERATION_RESULT, + send_notification(debug_NotificationType_NOTIFICATION_OPERATION_RESULT, debug_OperationResult_fields, &result); } -void Debugger::addBreakpoint(uint8_t *loc) { this->breakpoints.insert(loc); } +void Debugger::add_breakpoint(uint8_t *loc) { this->breakpoints.insert(loc); } -void Debugger::deleteBreakpoint(uint8_t *loc) { this->breakpoints.erase(loc); } +void Debugger::delete_breakpoint(uint8_t *loc) { this->breakpoints.erase(loc); } // ReSharper disable once CppParameterMayBeConstPtrOrRef // incorrect warning -bool Debugger::isBreakpoint(uint8_t *loc) { +bool Debugger::is_breakpoint(uint8_t *loc) { return this->breakpoints.find(loc) != this->breakpoints.end() || this->mark == loc; } -void Debugger::notifyBreakpoint(Module *m, uint8_t *pc_ptr) { +void Debugger::notify_breakpoint(Module *m, uint8_t *pc_ptr) { if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m); mark = nullptr; debug_HitBreakpoint hit = debug_HitBreakpoint_init_zero; hit.has_location = true; hit.location.module_index = 0; hit.location.program_counter = toVirtualAddress(pc_ptr, m); - sendNotification(debug_NotificationType_NOTIFICATION_HIT_BREAKPOINT, + send_notification(debug_NotificationType_NOTIFICATION_HIT_BREAKPOINT, debug_HitBreakpoint_fields, &hit); } @@ -225,13 +225,13 @@ void Debugger::notifyBreakpoint(Module *m, uint8_t *pc_ptr) { */ namespace { -bool collectBytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +bool collect_bytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); out->resize(stream->bytes_left); return out->empty() || pb_read(stream, out->data(), out->size()); } -[[maybe_unused]] bool collectWords(pb_istream_t *stream, +[[maybe_unused]] bool collect_words(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { @@ -242,12 +242,12 @@ bool collectBytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { return true; } -void setDecodeCallback(pb_callback_t *callback, std::vector *out) { - callback->funcs.decode = collectBytes; +void set_decode_callback(pb_callback_t *callback, std::vector *out) { + callback->funcs.decode = collect_bytes; callback->arg = out; } -bool collectVarints(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +bool collect_varints(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { uint64_t value = 0; @@ -262,14 +262,14 @@ struct DecodedCallbackEntry { std::string topic; std::vector indexes; }; -bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, +bool collect_callback_entries(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *entries = static_cast *>(*arg); debug_CallbackEntry entry = debug_CallbackEntry_init_zero; std::vector topic; std::vector indexes; - setDecodeCallback(&entry.topic, &topic); - entry.table_indexes.funcs.decode = collectVarints; + set_decode_callback(&entry.topic, &topic); + entry.table_indexes.funcs.decode = collect_varints; entry.table_indexes.arg = &indexes; if (!pb_decode(stream, debug_CallbackEntry_fields, &entry)) return false; entries->push_back( @@ -277,7 +277,7 @@ bool collectCallbackEntries(pb_istream_t *stream, const pb_field_iter_t *, return true; } -std::optional findImportedFunction(Module *m, +std::optional find_imported_function(Module *m, const std::string &name) { for (uint32_t index = 0; index < m->import_count; ++index) { if (m->functions[index].import_field != nullptr && @@ -287,7 +287,7 @@ std::optional findImportedFunction(Module *m, return std::nullopt; } -bool collectValues(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +bool collect_values(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); debug_Value value = debug_Value_init_zero; if (!pb_decode(stream, debug_Value_fields, &value)) return false; @@ -295,7 +295,7 @@ bool collectValues(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { return true; } -bool applyValueUpdate(const debug_Value &from, StackValue *to) { +bool apply_value_update(const debug_Value &from, StackValue *to) { switch (from.which_data) { case debug_Value_i32_bits_tag: to->value_type = I32; @@ -318,7 +318,7 @@ bool applyValueUpdate(const debug_Value &from, StackValue *to) { } } -[[maybe_unused]] void valueToProto(const StackValue &from, const uint32_t index, +[[maybe_unused]] void value_to_proto(const StackValue &from, const uint32_t index, debug_Value *to) { *to = debug_Value_init_zero; to->index = index; @@ -366,7 +366,7 @@ struct SnapshotView { *overrides; }; -ValueView currentLocals(const ExecutionContext *context) { +ValueView current_locals(const ExecutionContext *context) { if (context->csp < 0 || context->fp < 0) return {nullptr, 0, nullptr}; for (int index = context->csp; index >= 0; --index) { @@ -387,35 +387,35 @@ ValueView currentLocals(const ExecutionContext *context) { return {nullptr, 0, nullptr}; } -bool encodeValue(pb_ostream_t *stream, const pb_field_t *field, +bool encode_value(pb_ostream_t *stream, const pb_field_t *field, const StackValue &source, const size_t index) { debug_Value value = debug_Value_init_zero; - valueToProto(source, static_cast(index), &value); + value_to_proto(source, static_cast(index), &value); return pb_encode_tag_for_field(stream, field) && pb_encode_submessage(stream, debug_Value_fields, &value); } -bool encodeValueRange(pb_ostream_t *stream, const pb_field_t *field, +bool encode_value_range(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *view = static_cast(*arg); for (size_t index = 0; index < view->size; ++index) { const StackValue *value = view->globals == nullptr ? &view->values[index] : view->globals[index]->value; - if (!encodeValue(stream, field, *value, index)) return false; + if (!encode_value(stream, field, *value, index)) return false; } return true; } -bool encodeValues(pb_ostream_t *stream, const pb_field_t *field, +bool encode_values(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *values = static_cast *>(*arg); ValueView view{values->data(), values->size(), nullptr}; void *range = &view; - return encodeValueRange(stream, field, &range); + return encode_value_range(stream, field, &range); } -bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, +bool encode_bytes(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *bytes = static_cast *>(*arg); ByteView view{bytes->data(), bytes->size()}; @@ -423,7 +423,7 @@ bool encodeBytes(pb_ostream_t *stream, const pb_field_t *field, return nanopb_encoder::encode_bytes(stream, field, &opaque); } -bool encodeBreakpoints(pb_ostream_t *stream, const pb_field_t *field, +bool encode_breakpoints(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *view = static_cast(*arg); for (uint8_t *breakpoint : view->debugger->breakpoints) { @@ -435,7 +435,7 @@ bool encodeBreakpoints(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeFunctions(pb_ostream_t *stream, const pb_field_t *field, +bool encode_functions(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *view = static_cast(*arg); Module *module = view->module; @@ -467,7 +467,7 @@ bool encodeFunctions(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeCallstack(pb_ostream_t *stream, const pb_field_t *field, +bool encode_callstack(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *view = static_cast(*arg); const ExecutionContext *context = view->context; @@ -495,7 +495,7 @@ bool encodeCallstack(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeCallbackIndexes(pb_ostream_t *stream, const pb_field_t *field, +bool encode_callback_indexes(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *callbacks = static_cast *>(*arg); for (const Callback &callback : *callbacks) { @@ -507,7 +507,7 @@ bool encodeCallbackIndexes(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeCallbacks(pb_ostream_t *stream, const pb_field_t *field, +bool encode_callbacks(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *callbacks = static_cast(*arg); @@ -519,7 +519,7 @@ bool encodeCallbacks(pb_ostream_t *stream, const pb_field_t *field, entry.topic.funcs.encode = nanopb_encoder::encode_bytes; entry.topic.arg = &topicBytes; - entry.table_indexes.funcs.encode = encodeCallbackIndexes; + entry.table_indexes.funcs.encode = encode_callback_indexes; entry.table_indexes.arg = entries; if (!pb_encode_tag_for_field(stream, field) || @@ -530,7 +530,7 @@ bool encodeCallbacks(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeEvents(pb_ostream_t *stream, const pb_field_t *field, +bool encode_events(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *range = static_cast(*arg); @@ -557,7 +557,7 @@ bool encodeEvents(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeIOState(pb_ostream_t *stream, const pb_field_t *field, +bool encode_io_state(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *states = static_cast *>(*arg); @@ -577,7 +577,7 @@ bool encodeIOState(pb_ostream_t *stream, const pb_field_t *field, return true; } -bool encodeOverrides(pb_ostream_t *stream, const pb_field_t *field, +bool encode_overrides(pb_ostream_t *stream, const pb_field_t *field, void *const *arg) { const auto *view = static_cast(*arg); @@ -611,14 +611,14 @@ bool encodeOverrides(pb_ostream_t *stream, const pb_field_t *field, } // namespace -bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { - std::optional message = getDebugMessage(); +bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { + std::optional message = get_debug_message(); if (!message) return false; const auto malformed = [this]() { - sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + send_notification(debug_NotificationType_NOTIFICATION_MALFORMED); }; - const auto requireEmpty = [&message, &malformed]() { + const auto require_empty = [&message, &malformed]() { if (!message->payload.empty()) { malformed(); return false; @@ -628,34 +628,34 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { switch (message->type) { case debug_Command_COMMAND_RUN: - if (!requireEmpty()) break; - handleInterruptRUN(m, program_state); - sendNotification(debug_NotificationType_NOTIFICATION_CONTINUED); + if (!require_empty()) break; + handle_interrupt_run(m, program_state); + send_notification(debug_NotificationType_NOTIFICATION_CONTINUED); break; case debug_Command_COMMAND_HALT: - if (!requireEmpty()) break; - sendNotification(debug_NotificationType_NOTIFICATION_HALTED); + if (!require_empty()) break; + send_notification(debug_NotificationType_NOTIFICATION_HALTED); if (channel != nullptr) channel->close(); break; case debug_Command_COMMAND_PAUSE: - if (!requireEmpty()) break; - pauseRuntime(m); + if (!require_empty()) break; + pause_runtime(m); if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); - sendNotification(debug_NotificationType_NOTIFICATION_PAUSED); + send_notification(debug_NotificationType_NOTIFICATION_PAUSED); break; case debug_Command_COMMAND_STEP: - if (!requireEmpty()) break; - handleSTEP(m, program_state); + if (!require_empty()) break; + handle_step(m, program_state); break; case debug_Command_COMMAND_STEP_OVER: - if (!requireEmpty()) break; - handleSTEPOver(m, program_state); + if (!require_empty()) break; + handle_step_over(m, program_state); break; case debug_Command_COMMAND_ADD_BREAKPOINT: case debug_Command_COMMAND_REMOVE_BREAKPOINT: { debug_Breakpoint breakpoint = debug_Breakpoint_init_zero; - if (!decodePayload(message->payload, debug_Breakpoint_fields, + if (!decode_payload(message->payload, debug_Breakpoint_fields, &breakpoint) || !breakpoint.has_location || breakpoint.location.module_index != 0 || @@ -667,15 +667,15 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { uint8_t *address = toPhysicalAddress(breakpoint.location.program_counter, m); if (message->type == debug_Command_COMMAND_ADD_BREAKPOINT) - addBreakpoint(address); + add_breakpoint(address); else - deleteBreakpoint(address); - sendOperationResult(message->type, true); + delete_breakpoint(address); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_CONTINUE_FOR: { debug_ContinueFor request = debug_ContinueFor_init_zero; - if (!decodePayload(message->payload, debug_ContinueFor_fields, + if (!decode_payload(message->payload, debug_ContinueFor_fields, &request) || request.count == 0) { malformed(); @@ -683,13 +683,13 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } remaining_instructions = static_cast(request.count); *program_state = WARDUINOrun; - sendNotification(debug_NotificationType_NOTIFICATION_CONTINUED); + send_notification(debug_NotificationType_NOTIFICATION_CONTINUED); break; } case debug_Command_COMMAND_DUMP: - if (!requireEmpty()) break; - pauseRuntime(m); - encodeSnapshot(m, + if (!require_empty()) break; + pause_runtime(m); + encode_snapshot(m, snapshotPc | snapshotBreakpoints | snapshotCallstack | snapshotGlobals | snapshotTable | snapshotBranchTable | @@ -699,28 +699,28 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { debug_NotificationType_NOTIFICATION_SNAPSHOT); break; case debug_Command_COMMAND_DUMP_LOCALS: - if (!requireEmpty()) break; - pauseRuntime(m); - dumpLocals(m); + if (!require_empty()) break; + pause_runtime(m); + dump_locals(m); break; case debug_Command_COMMAND_SNAPSHOT: - if (!requireEmpty()) break; - pauseRuntime(m); + if (!require_empty()) break; + pause_runtime(m); snapshot(m); break; case debug_Command_COMMAND_DUMP_EVENTS: { debug_Range range = debug_Range_init_zero; - if (!decodePayload(message->payload, debug_Range_fields, &range) || + if (!decode_payload(message->payload, debug_Range_fields, &range) || range.end < range.start) { malformed(); break; } - dumpEvents(range.start, range.end - range.start); + dump_events(range.start, range.end - range.start); break; } case debug_Command_COMMAND_DUMP_CALLBACKS: - if (!requireEmpty()) break; - dumpCallbackmapping(); + if (!require_empty()) break; + dump_callback_mapping(); break; case debug_Command_COMMAND_UPDATE_LOCAL: { const auto update = update_value(message->payload); @@ -732,11 +732,11 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } StackValue *value = &context->stack[context->fp + update->index]; - if (!applyValueUpdate(update->value, value)) { + if (!apply_value_update(update->value, value)) { malformed(); break; } - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_UPDATE_GLOBAL: { @@ -746,11 +746,11 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { break; } StackValue *value = m->globals[update->index]->value; - if (!applyValueUpdate(update->value, value)) { + if (!apply_value_update(update->value, value)) { malformed(); break; } - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_UPDATE_STACK: { @@ -762,18 +762,18 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { break; } StackValue *value = &context->stack[update->index]; - if (!applyValueUpdate(update->value, value)) { + if (!apply_value_update(update->value, value)) { malformed(); break; } - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_UPDATE_MODULE: { debug_ModuleUpdate update = debug_ModuleUpdate_init_zero; std::vector wasm; - setDecodeCallback(&update.wasm, &wasm); - if (!decodePayload(message->payload, debug_ModuleUpdate_fields, + set_decode_callback(&update.wasm, &wasm); + if (!decode_payload(message->payload, debug_ModuleUpdate_fields, &update) || wasm.empty()) { malformed(); @@ -781,19 +781,19 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } auto *copy = static_cast(malloc(wasm.size())); if (copy == nullptr) { - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } memcpy(copy, wasm.data(), wasm.size()); m->warduino->update_module(m, copy, wasm.size()); - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_UPDATE_FUNCTION: { debug_Function update = debug_Function_init_zero; std::vector instructions; - setDecodeCallback(&update.instructions, &instructions); - if (!decodePayload(message->payload, debug_Function_fields, + set_decode_callback(&update.instructions, &instructions); + if (!decode_payload(message->payload, debug_Function_fields, &update) || update.function_index >= m->function_count || instructions.empty() || instructions.back() != 0x0b) { @@ -806,15 +806,15 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { function.end_ptr = function.start_ptr + functionBodies[update.function_index].size() - 1; function.br_ptr = function.end_ptr; - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_UPDATE_CALLBACKS: { debug_CallbackMapping mapping = debug_CallbackMapping_init_zero; std::vector entries; - mapping.entries.funcs.decode = collectCallbackEntries; + mapping.entries.funcs.decode = collect_callback_entries; mapping.entries.arg = &entries; - if (!decodePayload(message->payload, debug_CallbackMapping_fields, + if (!decode_payload(message->payload, debug_CallbackMapping_fields, &mapping)) { malformed(); break; @@ -825,20 +825,20 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { CallbackHandler::add_callback( Callback(m, entry.topic, index)); } - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_SET_SNAPSHOT_POLICY: { debug_SnapshotPolicyConfig config = debug_SnapshotPolicyConfig_init_zero; std::vector selectedState; - setDecodeCallback(&config.selected_state, &selectedState); + set_decode_callback(&config.selected_state, &selectedState); SnapshotSelection selectedMask = 0; - if (!decodePayload(message->payload, + if (!decode_payload(message->payload, debug_SnapshotPolicyConfig_fields, &config) || config.policy > debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING || - !parseSelection(selectedState.data(), selectedState.size(), + !parse_selection(selectedState.data(), selectedState.size(), &selectedMask)) { malformed(); break; @@ -853,7 +853,7 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { checkpoint_state = static_cast(malloc(selectedState.size())); if (checkpoint_state == nullptr) { - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } memcpy(checkpoint_state, selectedState.data(), @@ -861,7 +861,7 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } if (snapshotPolicy == SnapshotPolicy::checkpointing) checkpoint(m, true); - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_SET_OVERRIDE: @@ -869,88 +869,88 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { debug_Override request = debug_Override_init_zero; std::vector nameBytes; std::vector words; - setDecodeCallback(&request.primitive_name, &nameBytes); - request.argument_words.funcs.decode = collectWords; + set_decode_callback(&request.primitive_name, &nameBytes); + request.argument_words.funcs.decode = collect_words; request.argument_words.arg = &words; - if (!decodePayload(message->payload, debug_Override_fields, + if (!decode_payload(message->payload, debug_Override_fields, &request)) { malformed(); break; } - const auto fidx = findImportedFunction( + const auto fidx = find_imported_function( m, std::string(nameBytes.begin(), nameBytes.end())); if (!fidx || words.size() != m->functions[*fidx].type->param_count) { - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } words.push_back(*fidx); if (message->type == debug_Command_COMMAND_SET_OVERRIDE) overrides[words] = request.result; else if (overrides.erase(words) == 0) { - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_INSPECT: { debug_Inspect request = debug_Inspect_init_zero; std::vector selected; - setDecodeCallback(&request.state, &selected); - if (!decodePayload(message->payload, debug_Inspect_fields, + set_decode_callback(&request.state, &selected); + if (!decode_payload(message->payload, debug_Inspect_fields, &request)) { malformed(); break; } SnapshotSelection selection = 0; - if (!parseSelection(selected.data(), selected.size(), &selection)) { + if (!parse_selection(selected.data(), selected.size(), &selection)) { malformed(); break; } - pauseRuntime(m); - encodeSnapshot(m, selection, + pause_runtime(m); + encode_snapshot(m, selection, debug_NotificationType_NOTIFICATION_SNAPSHOT); break; } case debug_Command_COMMAND_LOAD_SNAPSHOT: { debug_Snapshot state = debug_Snapshot_init_zero; - if (!decodePayload(message->payload, debug_Snapshot_fields, + if (!decode_payload(message->payload, debug_Snapshot_fields, &state) || !isToPhysicalAddrPossible(state.program_counter, m)) { malformed(); break; } - pauseRuntime(m); + pause_runtime(m); m->warduino->execution_context->pc_ptr = toPhysicalAddress(state.program_counter, m); - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_ADD_PROXY: case debug_Command_COMMAND_REMOVE_PROXY: { debug_FunctionRef reference = debug_FunctionRef_init_zero; - if (!decodePayload(message->payload, debug_FunctionRef_fields, + if (!decode_payload(message->payload, debug_FunctionRef_fields, &reference) || supervisor == nullptr || reference.function_index >= m->function_count) { - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } if (message->type == debug_Command_COMMAND_ADD_PROXY) supervisor->registerProxiedCall(reference.function_index); else supervisor->unregisterProxiedCall(reference.function_index); - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; } case debug_Command_COMMAND_PROXY_CALL: case debug_Command_COMMAND_INVOKE: { debug_RemoteFunctionCall call = debug_RemoteFunctionCall_init_zero; std::vector values; - call.arguments.funcs.decode = collectValues; + call.arguments.funcs.decode = collect_values; call.arguments.arg = &values; - if (!decodePayload(message->payload, + if (!decode_payload(message->payload, debug_RemoteFunctionCall_fields, &call) || call.function_index >= m->function_count || values.size() != @@ -961,7 +961,7 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { auto *arguments = new StackValue[values.size()]; bool valid = true; for (size_t index = 0; index < values.size(); ++index) - valid &= valueFromProto(values[index], &arguments[index]); + valid &= value_from_proto(values[index], &arguments[index]); if (!valid) { delete[] arguments; malformed(); @@ -970,7 +970,7 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { if (message->type == debug_Command_COMMAND_PROXY_CALL) { if (proxy == nullptr) { delete[] arguments; - sendOperationResult(message->type, false); + send_operation_result(message->type, false); break; } proxy->pushRFC( @@ -993,36 +993,36 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { result.success = exception[0] == "\0"[0]; std::vector error; if (result.success) { - result.results.funcs.encode = encodeValues; + result.results.funcs.encode = encode_values; result.results.arg = const_cast *>(&results); } else { error.assign(exception, exception + std::strlen(exception)); - result.error.funcs.encode = encodeBytes; + result.error.funcs.encode = encode_bytes; result.error.arg = &error; } - sendNotification( + send_notification( debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, debug_RemoteFunctionResult_fields, &result); break; } case debug_Command_COMMAND_PROXIFY: - if (!requireEmpty()) break; + if (!require_empty()) break; proxify(); - sendOperationResult(message->type, true); + send_operation_result(message->type, true); break; case debug_Command_COMMAND_POP_EVENT: - if (!requireEmpty()) break; - sendOperationResult(message->type, + if (!require_empty()) break; + send_operation_result(message->type, CallbackHandler::resolve_event(true)); break; case debug_Command_COMMAND_PUSH_EVENT: { debug_Event event = debug_Event_init_zero; std::vector topic; std::vector payload; - setDecodeCallback(&event.topic, &topic); - setDecodeCallback(&event.payload, &payload); - if (!decodePayload(message->payload, debug_Event_fields, &event) || + set_decode_callback(&event.topic, &topic); + set_decode_callback(&event.payload, &payload); + if (!decode_payload(message->payload, debug_Event_fields, &event) || topic.empty()) { malformed(); break; @@ -1030,12 +1030,12 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { CallbackHandler::push_event( std::string(topic.begin(), topic.end()), reinterpret_cast(payload.data()), payload.size()); - notifyPushedEvent(); + notify_pushed_event(); break; } case debug_Command_COMMAND_RESET: - if (!requireEmpty()) break; - sendOperationResult(message->type, reset(m)); + if (!require_empty()) break; + send_operation_result(message->type, reset(m)); break; default: malformed(); @@ -1045,10 +1045,10 @@ bool Debugger::checkDebugMessages(Module *m, RunningState *program_state) { } // Private methods -void Debugger::printValue(const StackValue *, const uint32_t, +void Debugger::print_value(const StackValue *, const uint32_t, const bool) const {} -uint8_t *Debugger::findOpcode(Module *m, const Block *block) { +uint8_t *Debugger::find_opcode(Module *m, const Block *block) { const auto find = std::find_if(std::begin(m->block_lookup), std::end(m->block_lookup), [&](const std::pair &pair) { @@ -1065,7 +1065,7 @@ uint8_t *Debugger::findOpcode(Module *m, const Block *block) { return opcode; } -void Debugger::handleInvoke(Module *m, uint8_t *interruptData) const { +void Debugger::handle_invoke(Module *m, uint8_t *interruptData) const { const uint32_t fidx = read_LEB_32(&interruptData); if (fidx >= m->function_count) { @@ -1082,25 +1082,25 @@ void Debugger::handleInvoke(Module *m, uint8_t *interruptData) const { WARDuino::instance()->invoke(m, fidx, func.param_count, args); instance->program_state = current; - this->dumpStack(m); + this->dump_stack(m); } -void Debugger::handleInterruptRUN(const Module *m, +void Debugger::handle_interrupt_run(const Module *m, RunningState *program_state) { ExecutionContext *ectx = m->warduino->execution_context; - if (*program_state == WARDUINOpause && this->isBreakpoint(ectx->pc_ptr)) { + if (*program_state == WARDUINOpause && this->is_breakpoint(ectx->pc_ptr)) { this->skipBreakpoint = ectx->pc_ptr; } *program_state = WARDUINOrun; } -void Debugger::handleSTEP(const Module *m, RunningState *program_state) { +void Debugger::handle_step(const Module *m, RunningState *program_state) { ExecutionContext *ectx = m->warduino->execution_context; *program_state = WARDUINOstep; this->skipBreakpoint = ectx->pc_ptr; } -void Debugger::handleSTEPOver(const Module *m, RunningState *program_state) { +void Debugger::handle_step_over(const Module *m, RunningState *program_state) { ExecutionContext *ectx = m->warduino->execution_context; this->skipBreakpoint = ectx->pc_ptr; uint8_t const opcode = *ectx->pc_ptr; @@ -1118,19 +1118,19 @@ void Debugger::handleSTEPOver(const Module *m, RunningState *program_state) { *program_state = WARDUINOrun; } else { // normal step - this->handleSTEP(m, program_state); + this->handle_step(m, program_state); } } -void Debugger::handleInterruptBP(Module *m, uint8_t *interruptData) { +void Debugger::handle_interrupt_bp(Module *m, uint8_t *interruptData) { uint8_t *bpData = interruptData + 1; uint32_t virtualAddress = read_B32(&bpData); if (isToPhysicalAddrPossible(virtualAddress, m)) { uint8_t *bpt = toPhysicalAddress(virtualAddress, m); if (*interruptData == 0x06) { - this->addBreakpoint(bpt); + this->add_breakpoint(bpt); } else { - this->deleteBreakpoint(bpt); + this->delete_breakpoint(bpt); } } debug("BP %" PRIu32 "!\n", virtualAddress); @@ -1139,7 +1139,7 @@ void Debugger::handleInterruptBP(Module *m, uint8_t *interruptData) { std::optional Debugger::update_value( const std::vector &payload) const { debug_ValueUpdate update = debug_ValueUpdate_init_zero; - if (!decodePayload(payload, debug_ValueUpdate_fields, &update) || + if (!decode_payload(payload, debug_ValueUpdate_fields, &update) || !update.has_value) { return std::nullopt; } @@ -1148,38 +1148,38 @@ std::optional Debugger::update_value( void Debugger::dump(Module *m, bool) const { snapshot(m); } -void Debugger::dumpStack(const Module *m) const { +void Debugger::dump_stack(const Module *m) const { const ExecutionContext *ectx = m->warduino->execution_context; ValueView values{ectx->stack, ectx->sp >= 0 ? static_cast(ectx->sp + 1) : 0, nullptr}; /* ValueView points directly at the execution stack. */ debug_Locals locals = debug_Locals_init_zero; - locals.values.funcs.encode = encodeValueRange; + locals.values.funcs.encode = encode_value_range; locals.values.arg = &values; - sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, + send_notification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, debug_Locals_fields, &locals); } -void Debugger::dumpBreakpoints(Module *) const {} +void Debugger::dump_breakpoints(Module *) const {} -void Debugger::dumpFunctions(Module *) const {} +void Debugger::dump_functions(Module *) const {} /* * {"type":%u,"fidx":"0x%x","sp":%d,"fp":%d,"ra":"%p"}%s */ -void Debugger::dumpCallstack(Module *) const {} +void Debugger::dump_callstack(Module *) const {} -void Debugger::dumpLocals(const Module *m) const { - ValueView values = currentLocals(m->warduino->execution_context); +void Debugger::dump_locals(const Module *m) const { + ValueView values = current_locals(m->warduino->execution_context); debug_Locals locals = debug_Locals_init_zero; - locals.values.funcs.encode = encodeValueRange; + locals.values.funcs.encode = encode_value_range; locals.values.arg = &values; - sendNotification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, + send_notification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, debug_Locals_fields, &locals); } -void Debugger::dumpEvents(long start, long size) const { +void Debugger::dump_events(long start, long size) const { const size_t total = CallbackHandler::event_count(); const size_t first = std::min(start < 0 ? size_t{0} : static_cast(start), total); @@ -1191,23 +1191,23 @@ void Debugger::dumpEvents(long start, long size) const { queue.has_range = true; queue.range.start = static_cast(first); queue.range.end = static_cast(first + count); - queue.events.funcs.encode = encodeEvents; + queue.events.funcs.encode = encode_events; queue.events.arg = ⦥ - sendNotification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, + send_notification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, debug_EventsQueue_fields, &queue); } -void Debugger::dumpCallbackmapping() const { +void Debugger::dump_callback_mapping() const { const auto &callbacks = CallbackHandler::callback_map(); debug_CallbackMapping mapping = debug_CallbackMapping_init_zero; - mapping.entries.funcs.encode = encodeCallbacks; + mapping.entries.funcs.encode = encode_callbacks; mapping.entries.arg = const_cast(&callbacks); - sendNotification(debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP, + send_notification(debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP, debug_CallbackMapping_fields, &mapping); } -void Debugger::dumpHeapInfo(Module *) const {} +void Debugger::dump_heap_info(Module *) const {} /** * Read the change in bytes array. @@ -1216,7 +1216,7 @@ void Debugger::dumpHeapInfo(Module *) const {} * [0x10, index, ... new function body 0x0b] * Where index is the index without imports */ -bool Debugger::handleChangedFunction(const Module *m, uint8_t *bytes) { +bool Debugger::handle_changed_function(const Module *m, uint8_t *bytes) { // Check if this was a change request if (*bytes != interruptUPDATEFun) return false; @@ -1274,7 +1274,7 @@ bool Debugger::handleChangedFunction(const Module *m, uint8_t *bytes) { * @param bytes * @return */ -bool Debugger::handleChangedLocal(const Module *m, uint8_t *bytes) const { +bool Debugger::handle_changed_local(const Module *m, uint8_t *bytes) const { if (*bytes != interruptUPDATELocal) return false; uint8_t *pos = bytes + 1; debug("Local updates: %x\n", *pos); @@ -1303,13 +1303,13 @@ bool Debugger::handleChangedLocal(const Module *m, uint8_t *bytes) const { return true; } -void Debugger::notifyPushedEvent() const { - this->sendNotification(debug_NotificationType_NOTIFICATION_NEW_EVENT); +void Debugger::notify_pushed_event() const { + this->send_notification(debug_NotificationType_NOTIFICATION_NEW_EVENT); } -bool Debugger::handlePushedEvent(char *) const { return false; } +bool Debugger::handle_pushed_event(char *) const { return false; } -bool Debugger::parseSelection(const uint8_t *state, const size_t size, +bool Debugger::parse_selection(const uint8_t *state, const size_t size, SnapshotSelection *selection) { *selection = 0; for (size_t index = 0; index < size; ++index) { @@ -1319,7 +1319,7 @@ bool Debugger::parseSelection(const uint8_t *state, const size_t size, return true; } -bool Debugger::encodeSnapshot(Module *m, const SnapshotSelection selection, +bool Debugger::encode_snapshot(Module *m, const SnapshotSelection selection, const debug_NotificationType notification) const { ExecutionContext *ectx = m->warduino->execution_context; SnapshotView view{m, this, ectx, &overrides}; @@ -1346,33 +1346,33 @@ bool Debugger::encodeSnapshot(Module *m, const SnapshotSelection selection, } } if (selection & snapshotBreakpoints) { - state.breakpoints.funcs.encode = encodeBreakpoints; + state.breakpoints.funcs.encode = encode_breakpoints; state.breakpoints.arg = &view; } if (selection & snapshotFunctions) { - state.functions.funcs.encode = encodeFunctions; + state.functions.funcs.encode = encode_functions; state.functions.arg = &view; } if (selection & snapshotCallstack) { - state.callstack.funcs.encode = encodeCallstack; + state.callstack.funcs.encode = encode_callstack; state.callstack.arg = &view; } ValueView globals{nullptr, m->global_count, m->globals}; if (selection & snapshotGlobals) { - state.globals.funcs.encode = encodeValueRange; + state.globals.funcs.encode = encode_value_range; state.globals.arg = &globals; } ValueView stackValues{ectx->stack, ectx->sp >= 0 ? static_cast(ectx->sp + 1) : 0, nullptr}; if (selection & snapshotStack) { - state.stack.funcs.encode = encodeValueRange; + state.stack.funcs.encode = encode_value_range; state.stack.arg = &stackValues; } - ValueView locals = currentLocals(ectx); + ValueView locals = current_locals(ectx); if (selection & snapshotLocals) { state.has_locals = true; - state.locals.values.funcs.encode = encodeValueRange; + state.locals.values.funcs.encode = encode_value_range; state.locals.values.arg = &locals; } Uint32View table{m->table.entries, @@ -1405,7 +1405,7 @@ bool Debugger::encodeSnapshot(Module *m, const SnapshotSelection selection, const auto &callbacks = CallbackHandler::callback_map(); if (selection & snapshotCallbacks) { state.has_callbacks = true; - state.callbacks.entries.funcs.encode = encodeCallbacks; + state.callbacks.entries.funcs.encode = encode_callbacks; state.callbacks.entries.arg = const_cast(&callbacks); } @@ -1417,22 +1417,22 @@ bool Debugger::encodeSnapshot(Module *m, const SnapshotSelection selection, state.queue.has_range = true; state.queue.range.start = 0; state.queue.range.end = static_cast(eventCount); - state.queue.events.funcs.encode = encodeEvents; + state.queue.events.funcs.encode = encode_events; state.queue.events.arg = &events; } if (selection & snapshotIO) { ioState = m->warduino->interpreter->get_io_state(m); - state.io.funcs.encode = encodeIOState; + state.io.funcs.encode = encode_io_state; state.io.arg = &ioState; } if (selection & snapshotOverrides) { - state.overrides.funcs.encode = encodeOverrides; + state.overrides.funcs.encode = encode_overrides; state.overrides.arg = &view; } if (selection & snapshotHeap) state.heap_used = m->warduino->get_heap_used(); const bool sent = - sendNotification(notification, debug_Snapshot_fields, &state); + send_notification(notification, debug_Snapshot_fields, &state); for (IOStateElement *entry : ioState) delete entry; return sent; } @@ -1443,20 +1443,20 @@ void Debugger::snapshot(Module *m) const { snapshotTable | snapshotMemory | snapshotBranchTable | snapshotStack | snapshotCallbacks | snapshotEvents | snapshotIO | snapshotOverrides | snapshotHeap | snapshotFunctions | snapshotLocals; - encodeSnapshot(m, complete, debug_NotificationType_NOTIFICATION_SNAPSHOT); + encode_snapshot(m, complete, debug_NotificationType_NOTIFICATION_SNAPSHOT); } void Debugger::inspect(Module *m, const uint16_t size, const uint8_t *state) const { SnapshotSelection selection = 0; - if (!parseSelection(state, size, &selection)) { - sendNotification(debug_NotificationType_NOTIFICATION_MALFORMED); + if (!parse_selection(state, size, &selection)) { + send_notification(debug_NotificationType_NOTIFICATION_MALFORMED); return; } - encodeSnapshot(m, selection, debug_NotificationType_NOTIFICATION_SNAPSHOT); + encode_snapshot(m, selection, debug_NotificationType_NOTIFICATION_SNAPSHOT); } -void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) { +void Debugger::set_snapshot_policy(Module *m, uint8_t *interruptData) { uint8_t **data_ptr = &interruptData; if (*interruptData <= 2) { snapshotPolicy = SnapshotPolicy{*interruptData}; @@ -1491,7 +1491,7 @@ void Debugger::setSnapshotPolicy(Module *m, uint8_t *interruptData) { printf("ack%x\n", interruptSetSnapshotPolicy); } -std::optional getPrimitiveBeingCalled(Module *m, uint8_t *pc_ptr) { +std::optional get_primitive_being_called(Module *m, uint8_t *pc_ptr) { if (!pc_ptr) { return std::nullopt; } @@ -1508,12 +1508,12 @@ std::optional getPrimitiveBeingCalled(Module *m, uint8_t *pc_ptr) { return std::nullopt; } -void Debugger::handleSnapshotPolicy(Module *m) { +void Debugger::handle_snapshot_policy(Module *m) { if (snapshotPolicy == SnapshotPolicy::atEveryInstruction) { SnapshotSelection selection = 0; if (checkpoint_state != nullptr && - parseSelection(checkpoint_state, checkpoint_state_size, &selection)) - encodeSnapshot(m, selection, + parse_selection(checkpoint_state, checkpoint_state_size, &selection)) + encode_snapshot(m, selection, debug_NotificationType_NOTIFICATION_SNAPSHOT); } else if (snapshotPolicy == SnapshotPolicy::checkpointing) { if (instructions_executed >= checkpointInterval || fidx_called) { @@ -1533,7 +1533,7 @@ void Debugger::handleSnapshotPolicy(Module *m) { ExecutionContext *ectx = m->warduino->execution_context; // Store arguments of last primitive call. - if ((fidx_called = getPrimitiveBeingCalled(m, ectx->pc_ptr))) { + if ((fidx_called = get_primitive_being_called(m, ectx->pc_ptr))) { const Type *type = m->functions[*fidx_called].type; for (uint32_t i = 0; i < type->param_count; i++) { prim_args[type->param_count - i - 1] = @@ -1557,7 +1557,7 @@ void Debugger::checkpoint(Module *m, const bool force) { SnapshotSelection selection = 0; if (checkpoint_state != nullptr && - parseSelection(checkpoint_state, checkpoint_state_size, &selection) && + parse_selection(checkpoint_state, checkpoint_state_size, &selection) && selection != 0) { // Checkpoints only materialize the requested fields. Keep their views // on this stack through nanopb sizing and encoding. @@ -1574,7 +1574,7 @@ void Debugger::checkpoint(Module *m, const bool force) { ValueView globals{nullptr, m->global_count, m->globals}; /* Globals are read directly while nanopb encodes this checkpoint. */ if (selection & snapshotGlobals) { - notification.snapshot.globals.funcs.encode = encodeValueRange; + notification.snapshot.globals.funcs.encode = encode_value_range; notification.snapshot.globals.arg = &globals; } ValueView stack{ectx->stack, @@ -1583,18 +1583,18 @@ void Debugger::checkpoint(Module *m, const bool force) { /* Stack values are read directly while nanopb encodes this checkpoint. */ if (selection & snapshotStack) { - notification.snapshot.stack.funcs.encode = encodeValueRange; + notification.snapshot.stack.funcs.encode = encode_value_range; notification.snapshot.stack.arg = &stack; } if (selection & snapshotHeap) notification.snapshot.heap_used = m->warduino->get_heap_used(); } - sendNotification(debug_NotificationType_NOTIFICATION_CHECKPOINT, + send_notification(debug_NotificationType_NOTIFICATION_CHECKPOINT, debug_Checkpoint_fields, ¬ification); instructions_executed = 0; } -void Debugger::freeState(Module *m, uint8_t *interruptData) { +void Debugger::free_state(Module *m, uint8_t *interruptData) { debug("freeing the program state\n"); uint8_t *first_msg = nullptr; uint8_t *endfm = nullptr; @@ -1677,13 +1677,13 @@ void Debugger::freeState(Module *m, uint8_t *interruptData) { break; } default: - FATAL("freeState: receiving unknown command\n"); + FATAL("free_state: receiving unknown command\n"); } } debug("done with first msg\n"); } -bool Debugger::saveState(Module *m, uint8_t *interruptData) { +bool Debugger::save_state(Module *m, uint8_t *interruptData) { ExecutionContext *ectx = m->warduino->execution_context; uint8_t *program_state = nullptr; uint8_t *end_state = nullptr; @@ -1709,7 +1709,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { for (size_t i = 0; i < quantity_bps; i++) { auto virtualBP = read_B32(&program_state); if (isToPhysicalAddrPossible(virtualBP, m)) { - this->addBreakpoint(toPhysicalAddress(virtualBP, m)); + this->add_breakpoint(toPhysicalAddress(virtualBP, m)); } } break; @@ -1966,7 +1966,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { break; } default: { - FATAL("saveState: Received unknown program state\n"); + FATAL("save_state: Received unknown program state\n"); } } } @@ -1974,7 +1974,7 @@ bool Debugger::saveState(Module *m, uint8_t *interruptData) { return done == static_cast(1); } -uintptr_t Debugger::readPointer(uint8_t **data) { +uintptr_t Debugger::read_pointer(uint8_t **data) { const uint8_t len = (*data)[0]; uintptr_t bp = 0x0; for (size_t i = 0; i < len; i++) { @@ -1990,7 +1990,7 @@ void Debugger::proxify() { this->proxy = new Proxy(); // TODO delete } -void Debugger::handleProxyCall(Module *m, RunningState *, +void Debugger::handle_proxy_call(Module *m, RunningState *, uint8_t *interruptData) const { if (this->proxy == nullptr) { dbg_info("No proxy available to send proxy call to.\n"); @@ -2009,31 +2009,31 @@ void Debugger::handleProxyCall(Module *m, RunningState *, this->proxy->pushRFC(m, rfc); } -RFC *Debugger::topProxyCall() const { +RFC *Debugger::top_proxy_call() const { if (proxy == nullptr) { return nullptr; } return this->proxy->topRFC(); } -void Debugger::sendProxyCallResult(Module *m) const { +void Debugger::send_proxy_call_result(Module *m) const { if (proxy == nullptr) return; RFC *rfc = proxy->returnResult(m); if (rfc == nullptr) return; debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; result.success = rfc->success; - sendNotification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, + send_notification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, debug_RemoteFunctionResult_fields, &result); delete rfc; } -bool Debugger::isProxy() const { return this->proxy != nullptr; } +bool Debugger::is_proxy() const { return this->proxy != nullptr; } -bool Debugger::isProxied(const uint32_t fidx) const { - return this->supervisor != nullptr && this->supervisor->isProxied(fidx); +bool Debugger::is_proxied(const uint32_t fidx) const { + return this->supervisor != nullptr && this->supervisor->is_proxied(fidx); } -void Debugger::handleMonitorProxies(const Module *m, +void Debugger::handle_monitor_proxies(const Module *m, uint8_t *interruptData) const { const uint32_t amount_funcs = read_B32(&interruptData); printf("funcs_total %" PRIu32 "\n", amount_funcs); @@ -2048,7 +2048,7 @@ void Debugger::handleMonitorProxies(const Module *m, debug("done!\n"); } -void Debugger::startProxySupervisor(Channel *socket) { +void Debugger::start_proxy_supervisor(Channel *socket) { this->connected_to_proxy = true; this->supervisor = new ProxySupervisor(socket, this->supervisor_mutex); printf("Connected to proxy.\n"); @@ -2065,7 +2065,7 @@ void Debugger::disconnect_proxy() const { this->supervisor->thread.join(); } -void Debugger::updateCallbackmapping(Module *, const char *) { +void Debugger::update_callback_mapping(Module *, const char *) { // Legacy JSON callback mapping input is intentionally unsupported. } @@ -2078,12 +2078,12 @@ void Debugger::stop() { } // -void Debugger::pauseRuntime(const Module *m) { +void Debugger::pause_runtime(const Module *m) { m->warduino->program_state = WARDUINOpause; this->mark = nullptr; } -bool Debugger::handleUpdateModule(Module *m, uint8_t *data) { +bool Debugger::handle_update_module(Module *m, uint8_t *data) { uint8_t *wasm_data = data + 1; const uint32_t wasm_len = read_LEB_32(&wasm_data); auto *wasm = static_cast(malloc(sizeof(uint8_t) * wasm_len)); @@ -2093,7 +2093,7 @@ bool Debugger::handleUpdateModule(Module *m, uint8_t *data) { return true; } -bool Debugger::handleUpdateGlobalValue(const Module *m, uint8_t *data) const { +bool Debugger::handle_update_global_value(const Module *m, uint8_t *data) const { debug("Global updates: %x\n", *data); const uint32_t index = read_LEB_32(&data); @@ -2107,7 +2107,7 @@ bool Debugger::handleUpdateGlobalValue(const Module *m, uint8_t *data) const { return true; } -bool Debugger::handleUpdateStackValue(const Module *m, uint8_t *bytes) const { +bool Debugger::handle_update_stack_value(const Module *m, uint8_t *bytes) const { const uint32_t idx = read_LEB_32(&bytes); if (idx >= STACK_SIZE) { return false; @@ -2150,7 +2150,7 @@ std::string read_string(uint8_t **pos) { return str; } -void Debugger::addOverride(Module *m, uint8_t *interruptData) { +void Debugger::add_override(Module *m, uint8_t *interruptData) { const std::string primitive_name = read_string(&interruptData); const std::optional fidx = resolve_imported_function(m, primitive_name); @@ -2173,7 +2173,7 @@ void Debugger::addOverride(Module *m, uint8_t *interruptData) { overrides[key] = result; } -void Debugger::removeOverride(Module *m, uint8_t *interruptData) { +void Debugger::remove_override(Module *m, uint8_t *interruptData) { const std::string primitive_name = read_string(&interruptData); const std::optional fidx = resolve_imported_function(m, primitive_name); @@ -2198,7 +2198,7 @@ void Debugger::removeOverride(Module *m, uint8_t *interruptData) { debug("ack%x;1\n", interruptUnsetOverridePinValue); } -bool Debugger::getMockForArgs(Module *m, uint32_t fidx, uint32_t &result) { +bool Debugger::get_mock_for_args(Module *m, uint32_t fidx, uint32_t &result) { const uint32_t param_count = m->functions[fidx].type->param_count; std::vector key(param_count + 1); const ExecutionContext *ectx = m->warduino->execution_context; @@ -2214,7 +2214,7 @@ bool Debugger::getMockForArgs(Module *m, uint32_t fidx, uint32_t &result) { return true; } -bool Debugger::handleContinueFor(Module *m) { +bool Debugger::handle_continue_for(Module *m) { if (remaining_instructions < 0) return false; if (remaining_instructions == 0) { @@ -2222,21 +2222,21 @@ bool Debugger::handleContinueFor(Module *m) { if (snapshotPolicy == SnapshotPolicy::checkpointing) { checkpoint(m); } - this->sendNotification(debug_NotificationType_NOTIFICATION_PAUSED); - pauseRuntime(m); + this->send_notification(debug_NotificationType_NOTIFICATION_PAUSED); + pause_runtime(m); return true; } remaining_instructions--; return false; } -void Debugger::notifyCompleteStep(Module *m) const { +void Debugger::notify_complete_step(Module *m) const { // Upon completing a step in checkpointing mode, make a checkpoint. - if (m->warduino->debugger->getSnapshotPolicy() == + if (m->warduino->debugger->get_snapshot_policy() == SnapshotPolicy::checkpointing) { m->warduino->debugger->checkpoint(m); } - this->sendNotification(debug_NotificationType_NOTIFICATION_STEPPED); + this->send_notification(debug_NotificationType_NOTIFICATION_STEPPED); } Debugger::~Debugger() { diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 0f9e48801..52c1cacf6 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -189,70 +189,70 @@ class Debugger { // Private methods - void printValue(const StackValue *v, uint32_t idx, bool end) const; + void print_value(const StackValue *v, uint32_t idx, bool end) const; // TODO Move parsing to WARDuino class? - void parseDebugBuffer(size_t len, const uint8_t *buff); + void parse_debug_buffer(size_t len, const uint8_t *buff); - void pushMessage(DebugMessage msg); + void push_message(DebugMessage msg); - bool sendNotification(debug_NotificationType type, + bool send_notification(debug_NotificationType type, const pb_msgdesc_t *fields = nullptr, const void *payload = nullptr) const; - void sendOperationResult(debug_Command command, bool success) const; + void send_operation_result(debug_Command command, bool success) const; //// Handle REPL interrupts - void handleInvoke(Module *m, uint8_t *interruptData) const; + void handle_invoke(Module *m, uint8_t *interruptData) const; //// Handle Interrupt Types - void handleInterruptRUN(const Module *m, RunningState *program_state); + void handle_interrupt_run(const Module *m, RunningState *program_state); - void handleSTEP(const Module *m, RunningState *program_state); + void handle_step(const Module *m, RunningState *program_state); - void handleSTEPOver(const Module *m, RunningState *program_state); + void handle_step_over(const Module *m, RunningState *program_state); - void handleInterruptBP(Module *m, uint8_t *interruptData); + void handle_interrupt_bp(Module *m, uint8_t *interruptData); //// Information dumps void dump(Module *m, bool full = false) const; - void dumpStack(const Module *m) const; + void dump_stack(const Module *m) const; - void dumpLocals(const Module *m) const; + void dump_locals(const Module *m) const; - void dumpBreakpoints(Module *m) const; + void dump_breakpoints(Module *m) const; - void dumpFunctions(Module *m) const; + void dump_functions(Module *m) const; - void dumpCallstack(Module *m) const; + void dump_callstack(Module *m) const; - void dumpEvents(long start, long size) const; + void dump_events(long start, long size) const; - void dumpCallbackmapping() const; + void dump_callback_mapping() const; - void dumpHeapInfo(Module *m) const; + void dump_heap_info(Module *m) const; void inspect(Module *m, uint16_t sizeStateArray, const uint8_t *state) const; - bool encodeSnapshot(Module *m, SnapshotSelection selection, + bool encode_snapshot(Module *m, SnapshotSelection selection, debug_NotificationType notification) const; - static bool parseSelection(const uint8_t *state, size_t size, + static bool parse_selection(const uint8_t *state, size_t size, SnapshotSelection *selection); //// Handle live code update - static bool handleChangedFunction(const Module *m, uint8_t *bytes); + static bool handle_changed_function(const Module *m, uint8_t *bytes); - bool handleChangedLocal(const Module *m, uint8_t *bytes) const; + bool handle_changed_local(const Module *m, uint8_t *bytes) const; - static bool handleUpdateModule(Module *m, uint8_t *data); + static bool handle_update_module(Module *m, uint8_t *data); - bool handleUpdateGlobalValue(const Module *m, uint8_t *data) const; + bool handle_update_global_value(const Module *m, uint8_t *data) const; - bool handleUpdateStackValue(const Module *m, uint8_t *bytes) const; + bool handle_update_stack_value(const Module *m, uint8_t *bytes) const; std::optional update_value( const std::vector &payload) const; @@ -261,20 +261,20 @@ class Debugger { //// Handle mocking - void addOverride(Module *m, uint8_t *interruptData); - void removeOverride(Module *m, uint8_t *interruptData); + void add_override(Module *m, uint8_t *interruptData); + void remove_override(Module *m, uint8_t *interruptData); //// Handle out-of-place debugging - void freeState(Module *m, uint8_t *interruptData); + void free_state(Module *m, uint8_t *interruptData); - static uint8_t *findOpcode(Module *m, const Block *block); + static uint8_t *find_opcode(Module *m, const Block *block); - bool saveState(Module *m, uint8_t *interruptData); + bool save_state(Module *m, uint8_t *interruptData); - static uintptr_t readPointer(uint8_t **data); + static uintptr_t read_pointer(uint8_t **data); - static void updateCallbackmapping(Module *m, const char *interruptData); + static void update_callback_mapping(Module *m, const char *interruptData); bool operation(Module *m, operation op); @@ -297,59 +297,59 @@ class Debugger { ~Debugger(); - void setChannel(Channel *duplex); + void set_channel(Channel *duplex); // Public methods void stop(); - void pauseRuntime(const Module *m); // pause runtime for given module + void pause_runtime(const Module *m); // pause runtime for given module - void notifyCompleteStep( + void notify_complete_step( Module *m) const; // notify the debugger frontend that a step was taken // Interrupts - void addDebugMessage(size_t len, const uint8_t *buff); + void add_debug_message(size_t len, const uint8_t *buff); - std::optional getDebugMessage(); + std::optional get_debug_message(); - bool checkDebugMessages(Module *m, RunningState *program_state); + bool check_debug_messages(Module *m, RunningState *program_state); // Breakpoints - void addBreakpoint(uint8_t *loc); + void add_breakpoint(uint8_t *loc); - void deleteBreakpoint(uint8_t *loc); + void delete_breakpoint(uint8_t *loc); - bool isBreakpoint(uint8_t *loc); + bool is_breakpoint(uint8_t *loc); - void notifyBreakpoint(Module *m, uint8_t *pc_ptr); + void notify_breakpoint(Module *m, uint8_t *pc_ptr); // Out-of-place debugging: EDWARD void snapshot(Module *m) const; - void setSnapshotPolicy(Module *m, uint8_t *interruptData); + void set_snapshot_policy(Module *m, uint8_t *interruptData); - void handleSnapshotPolicy(Module *m); + void handle_snapshot_policy(Module *m); - bool handleContinueFor(Module *m); + bool handle_continue_for(Module *m); void proxify(); - void handleProxyCall(Module *m, RunningState *program_state, + void handle_proxy_call(Module *m, RunningState *program_state, uint8_t *interruptData) const; - RFC *topProxyCall() const; + RFC *top_proxy_call() const; - void sendProxyCallResult(Module *m) const; + void send_proxy_call_result(Module *m) const; - bool isProxy() const; + bool is_proxy() const; - bool isProxied(uint32_t fidx) const; + bool is_proxied(uint32_t fidx) const; - void startProxySupervisor(Channel *socket); + void start_proxy_supervisor(Channel *socket); bool proxy_connected() const; @@ -357,18 +357,18 @@ class Debugger { // Pull-based - void handleMonitorProxies(const Module *m, uint8_t *interruptData) const; + void handle_monitor_proxies(const Module *m, uint8_t *interruptData) const; // Push-based - void notifyPushedEvent() const; + void notify_pushed_event() const; - bool handlePushedEvent(char *bytes) const; + bool handle_pushed_event(char *bytes) const; // Concolic Multiverse Debugging - bool getMockForArgs(Module *m, uint32_t fidx, uint32_t &result); + bool get_mock_for_args(Module *m, uint32_t fidx, uint32_t &result); // Checkpointing void checkpoint(Module *m, bool force = false); - inline SnapshotPolicy getSnapshotPolicy() { return snapshotPolicy; } + inline SnapshotPolicy get_snapshot_policy() { return snapshotPolicy; } }; diff --git a/src/Edward/proxy.cpp b/src/Edward/proxy.cpp index 1b92d2482..3182874fa 100644 --- a/src/Edward/proxy.cpp +++ b/src/Edward/proxy.cpp @@ -34,7 +34,7 @@ void Proxy::pushRFC(Module *m, RFC *rfc) { ((Primitive)m->functions[rfc->fidx].func_ptr)(m); // send result directly m->warduino->program_state = PROXYhalt; - m->warduino->debugger->sendProxyCallResult(m); + m->warduino->debugger->send_proxy_call_result(m); return; } diff --git a/src/Edward/proxy_supervisor.cpp b/src/Edward/proxy_supervisor.cpp index ba4246d88..625bc9513 100644 --- a/src/Edward/proxy_supervisor.cpp +++ b/src/Edward/proxy_supervisor.cpp @@ -46,7 +46,7 @@ void runSupervisor(ProxySupervisor *supervisor) { } Event *parseJSON(char *buff) { - // TODO duplicate code in Debugger::handlePushedEvent + // TODO duplicate code in Debugger::handle_pushed_event nlohmann::basic_json<> parsed = nlohmann::json::parse(buff); printf("parseJSON: %s\n", parsed.dump().c_str()); std::string payload = *parsed.find("payload"); @@ -108,7 +108,7 @@ void ProxySupervisor::listenToSocket() { if (isEvent(parsed)) { CallbackHandler::push_event(new Event( *parsed.find("topic"), *parsed.find("payload"))); - WARDuino::instance()->debugger->notifyPushedEvent(); + WARDuino::instance()->debugger->notify_pushed_event(); } if (isReply(parsed)) { @@ -291,6 +291,6 @@ void ProxySupervisor::unregisterProxiedCall(uint32_t fidx) { void ProxySupervisor::unregisterAllProxiedCalls() { this->proxied->clear(); } -bool ProxySupervisor::isProxied(uint32_t fidx) { +bool ProxySupervisor::is_proxied(uint32_t fidx) { return this->proxied->count(fidx) > 0; } diff --git a/src/Edward/proxy_supervisor.h b/src/Edward/proxy_supervisor.h index 40b0bb55c..8cf5e3143 100644 --- a/src/Edward/proxy_supervisor.h +++ b/src/Edward/proxy_supervisor.h @@ -43,5 +43,5 @@ class ProxySupervisor { void registerProxiedCall(uint32_t fidx); void unregisterProxiedCall(uint32_t fidx); void unregisterAllProxiedCalls(); - bool isProxied(uint32_t fidx); + bool is_proxied(uint32_t fidx); }; diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index 8be3a3db6..470a18dfd 100644 --- a/src/Interpreter/instructions.cpp +++ b/src/Interpreter/instructions.cpp @@ -296,7 +296,7 @@ bool i_instr_call(Module *m) { ExecutionContext *ectx = m->warduino->execution_context; uint32_t fidx = read_LEB_32(&ectx->pc_ptr); - if (m->warduino->debugger->isProxied(fidx)) { + if (m->warduino->debugger->is_proxied(fidx)) { return proxy_call(m, fidx); } @@ -306,7 +306,7 @@ bool i_instr_call(Module *m) { // Mocking only works on primitives, no need to check for it otherwise. if (ectx->sp >= 0) { uint32_t mock_result; - if (m->warduino->debugger->getMockForArgs(m, fidx, mock_result)) { + if (m->warduino->debugger->get_mock_for_args(m, fidx, mock_result)) { const uint32_t param_count = m->functions[fidx].type->param_count; ectx->sp -= static_cast(param_count) - 1; diff --git a/src/Interpreter/interpreter.cpp b/src/Interpreter/interpreter.cpp index e2b157cc9..84630dd60 100644 --- a/src/Interpreter/interpreter.cpp +++ b/src/Interpreter/interpreter.cpp @@ -37,7 +37,7 @@ Block *Interpreter::pop_block(Module *m) { if (frame->block->block_type == 0xfe) { m->warduino->program_state = PROXYhalt; - m->warduino->debugger->sendProxyCallResult(m); + m->warduino->debugger->send_proxy_call_result(m); // free if proxy guard free(frame->block); frame->block = nullptr; @@ -135,7 +135,7 @@ uint32_t STORE_SIZE[] = {4, 8, 4, 8, 1, 2, 1, 2, 4}; bool Interpreter::store(Module *m, uint8_t type, uint32_t addr, StackValue &sval) { - if (m->warduino->debugger->isProxy()) { + if (m->warduino->debugger->is_proxy()) { return m->warduino->debugger; } @@ -227,12 +227,12 @@ bool Interpreter::interpret(Module *m, bool waiting) { m = ectx->current_module; if (m->warduino->program_state == WARDUINOstep) { - m->warduino->debugger->notifyCompleteStep(m); - m->warduino->debugger->pauseRuntime(m); + m->warduino->debugger->notify_complete_step(m); + m->warduino->debugger->pause_runtime(m); } while (m->warduino->program_state != WARDUINOinit && - m->warduino->debugger->checkDebugMessages( + m->warduino->debugger->check_debug_messages( m, &m->warduino->program_state)) { } fflush(stdout); @@ -258,22 +258,22 @@ bool Interpreter::interpret(Module *m, bool waiting) { // Program state is not paused // If BP and not the one we just unpaused - if (m->warduino->debugger->isBreakpoint(ectx->pc_ptr) && + if (m->warduino->debugger->is_breakpoint(ectx->pc_ptr) && m->warduino->debugger->skipBreakpoint != ectx->pc_ptr && m->warduino->program_state != PROXYrun) { - m->warduino->debugger->pauseRuntime(m); - m->warduino->debugger->notifyBreakpoint(m, ectx->pc_ptr); + m->warduino->debugger->pause_runtime(m); + m->warduino->debugger->notify_breakpoint(m, ectx->pc_ptr); continue; } m->warduino->debugger->skipBreakpoint = nullptr; - if (m->warduino->debugger->handleContinueFor(m)) { + if (m->warduino->debugger->handle_continue_for(m)) { continue; } // Take snapshot before executing an instruction if (m->warduino->program_state != WARDUINOinit) { - m->warduino->debugger->handleSnapshotPolicy(m); + m->warduino->debugger->handle_snapshot_policy(m); } opcode = *ectx->pc_ptr; @@ -477,11 +477,11 @@ bool Interpreter::interpret(Module *m, bool waiting) { if (m->warduino->program_state == PROXYrun) { dbg_info("Trap was thrown during proxy call.\n"); - RFC *rfc = m->warduino->debugger->topProxyCall(); + RFC *rfc = m->warduino->debugger->top_proxy_call(); rfc->success = false; rfc->exception = strdup(exception); rfc->exception_size = strlen(exception); - m->warduino->debugger->sendProxyCallResult(m); + m->warduino->debugger->send_proxy_call_result(m); } // Resolve all unhandled callback events diff --git a/src/WARDuino/CallbackHandler.cpp b/src/WARDuino/CallbackHandler.cpp index 3ee13560b..4f9796a3a 100644 --- a/src/WARDuino/CallbackHandler.cpp +++ b/src/WARDuino/CallbackHandler.cpp @@ -95,7 +95,7 @@ bool CallbackHandler::resolve_event(bool force) { if (should_push_event()) { Event e = CallbackHandler::events->at(CallbackHandler::pushed_cursor++); - WARDuino::instance()->debugger->notifyPushedEvent(); + WARDuino::instance()->debugger->notify_pushed_event(); CallbackHandler::events->pop_front(); CallbackHandler::pushed_cursor--; diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 1f411eb6c..d922ecd7c 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -1127,7 +1127,7 @@ int WARDuino::run_module(Module *m) { fflush(stdout); // wait - m->warduino->debugger->pauseRuntime(m); + m->warduino->debugger->pause_runtime(m); return interpreter->interpret(m, true); } @@ -1136,7 +1136,7 @@ int WARDuino::run_module(Module *m) { // parse numer per 2 chars (HEX) (stop if non-hex) // Don't use print in interrupt handlers void WARDuino::handleInterrupt(size_t len, uint8_t *buff) const { - this->debugger->addDebugMessage(len, buff); + this->debugger->add_debug_message(len, buff); } WARDuino *WARDuino::singleton = nullptr; @@ -1242,7 +1242,7 @@ void WARDuino::reset_module(Module *m) { } // wait - debugger->pauseRuntime(m); + debugger->pause_runtime(m); } void WARDuino::update_module(Module *m, uint8_t *wasm, uint32_t wasm_len) { @@ -1266,7 +1266,7 @@ void WARDuino::update_module(Module *m, uint8_t *wasm, uint32_t wasm_len) { } // wait - m->warduino->debugger->pauseRuntime(m); + m->warduino->debugger->pause_runtime(m); } uint32_t WARDuino::get_main_fidx(Module *m) { From e1a19537fe471fa513c39ff04e9aa66f41ce35e6 Mon Sep 17 00:00:00 2001 From: Tom Lauwaerts Date: Wed, 2 Sep 2026 14:18:44 +0200 Subject: [PATCH 26/26] Lint + fix build --- benchmarks/edward.ino.template | 2 +- platforms/Arduino/Arduino.ino.template | 2 +- platforms/CLI-Emulator/main.cpp | 6 +- platforms/ESP-IDF/main.cpp | 2 +- platforms/Zephyr/main.cpp | 2 +- src/Debug/debugger.cpp | 154 +++++++++++++------------ src/Debug/debugger.h | 10 +- src/Interpreter/instructions.cpp | 3 +- tests/compilation/esp32/esp32.ino | 2 +- tutorials/wat/main/main.cpp | 2 +- 10 files changed, 96 insertions(+), 89 deletions(-) diff --git a/benchmarks/edward.ino.template b/benchmarks/edward.ino.template index e566c6b7f..eb9e66b3e 100644 --- a/benchmarks/edward.ino.template +++ b/benchmarks/edward.ino.template @@ -34,7 +34,7 @@ void ICACHE_RAM_ATTR handleInput() { void startDebuggerStd(void* pvParameter) { int valread; uint8_t buffer[1024] = {0}; - wac->debugger->setChannel(fileno(stdout)); + wac->debugger->set_channel(fileno(stdout)); write(fileno(stdout), "Got a message ... \n", 19); while (true) { // taskYIELD(); diff --git a/platforms/Arduino/Arduino.ino.template b/platforms/Arduino/Arduino.ino.template index 59f838950..a254cad86 100644 --- a/platforms/Arduino/Arduino.ino.template +++ b/platforms/Arduino/Arduino.ino.template @@ -26,7 +26,7 @@ Module* m; void startDebuggerStd(void* pvParameter) { Channel* sink = new Sink(stdout); - wac->debugger->setChannel(sink); + wac->debugger->set_channel(sink); sink->open(); uint8_t buffer[1024] = {0}; diff --git a/platforms/CLI-Emulator/main.cpp b/platforms/CLI-Emulator/main.cpp index df626ec1b..e67944c49 100644 --- a/platforms/CLI-Emulator/main.cpp +++ b/platforms/CLI-Emulator/main.cpp @@ -187,7 +187,7 @@ void setupDebuggerCommunication(debugger_options &options) { duplex = new WebSocket(options.socket); } - wac->debugger->setChannel(duplex); + wac->debugger->set_channel(duplex); } const std::map &baudrateMap() { @@ -445,7 +445,7 @@ int main(int argc, const char *argv[]) { } if (initiallyPaused) { - wac->debugger->pauseRuntime(m); + wac->debugger->pause_runtime(m); } if (m) { @@ -548,7 +548,7 @@ int main(int argc, const char *argv[]) { } // Start supervising proxy device (new thread) - wac->debugger->startProxySupervisor(connection); + wac->debugger->start_proxy_supervisor(connection); } // Start debugger (new thread) diff --git a/platforms/ESP-IDF/main.cpp b/platforms/ESP-IDF/main.cpp index e49a9d3f6..a19838da0 100644 --- a/platforms/ESP-IDF/main.cpp +++ b/platforms/ESP-IDF/main.cpp @@ -38,7 +38,7 @@ std::vector loaded_modules; void startDebuggerStd(void* pvParameter) { Channel* duplex = new Duplex(stdin, stdout); - wac->debugger->setChannel(duplex); + wac->debugger->set_channel(duplex); duplex->open(); int valread; diff --git a/platforms/Zephyr/main.cpp b/platforms/Zephyr/main.cpp index 5e43ed0d2..3b5370452 100644 --- a/platforms/Zephyr/main.cpp +++ b/platforms/Zephyr/main.cpp @@ -71,7 +71,7 @@ std::vector loaded_modules; void startDebuggerStd() { Channel *duplex = new Duplex(stdin, stdout); - wac->debugger->setChannel(duplex); + wac->debugger->set_channel(duplex); duplex->open(); war_console_init(); diff --git a/src/Debug/debugger.cpp b/src/Debug/debugger.cpp index 6d7566caa..11b8a4576 100644 --- a/src/Debug/debugger.cpp +++ b/src/Debug/debugger.cpp @@ -37,7 +37,7 @@ void Debugger::set_channel(Channel *duplex) { namespace { bool decode_frame_length(const std::vector &bytes, size_t *headerSize, - size_t *payloadSize) { + size_t *payloadSize) { if (bytes.size() < 2) return false; uint32_t value = 0; for (size_t i = 0; i < 5; ++i) { @@ -68,7 +68,7 @@ bool is_known_command(const uint8_t type) { template bool decode_payload(const std::vector &payload, - const pb_msgdesc_t *fields, T *message) { + const pb_msgdesc_t *fields, T *message) { pb_istream_t stream = pb_istream_from_buffer(payload.data(), payload.size()); return pb_decode(&stream, fields, message); @@ -105,7 +105,8 @@ void Debugger::parse_debug_buffer(const size_t len, const uint8_t *buff) { if (!completeLength) { if (headerSize == SIZE_MAX || pendingFrameBytes.size() >= 6) { pendingFrameBytes.clear(); - send_notification(debug_NotificationType_NOTIFICATION_MALFORMED); + send_notification( + debug_NotificationType_NOTIFICATION_MALFORMED); } return; } @@ -143,8 +144,8 @@ std::optional Debugger::get_debug_message() { } bool Debugger::send_notification(const debug_NotificationType type, - const pb_msgdesc_t *fields, - const void *payload) const { + const pb_msgdesc_t *fields, + const void *payload) const { if (channel == nullptr) return false; size_t payloadSize = 0; if (fields != nullptr && payload != nullptr && @@ -173,12 +174,12 @@ bool Debugger::send_notification(const debug_NotificationType type, } void Debugger::send_operation_result(const debug_Command command, - const bool success) const { + const bool success) const { debug_OperationResult result = debug_OperationResult_init_zero; result.command = command; result.success = success; send_notification(debug_NotificationType_NOTIFICATION_OPERATION_RESULT, - debug_OperationResult_fields, &result); + debug_OperationResult_fields, &result); } void Debugger::add_breakpoint(uint8_t *loc) { this->breakpoints.insert(loc); } @@ -199,7 +200,7 @@ void Debugger::notify_breakpoint(Module *m, uint8_t *pc_ptr) { hit.location.module_index = 0; hit.location.program_counter = toVirtualAddress(pc_ptr, m); send_notification(debug_NotificationType_NOTIFICATION_HIT_BREAKPOINT, - debug_HitBreakpoint_fields, &hit); + debug_HitBreakpoint_fields, &hit); } /** @@ -232,7 +233,7 @@ bool collect_bytes(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { } [[maybe_unused]] bool collect_words(pb_istream_t *stream, - const pb_field_iter_t *, void **arg) { + const pb_field_iter_t *, void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { uint32_t value = 0; @@ -247,7 +248,8 @@ void set_decode_callback(pb_callback_t *callback, std::vector *out) { callback->arg = out; } -bool collect_varints(pb_istream_t *stream, const pb_field_iter_t *, void **arg) { +bool collect_varints(pb_istream_t *stream, const pb_field_iter_t *, + void **arg) { auto *out = static_cast *>(*arg); while (stream->bytes_left != 0) { uint64_t value = 0; @@ -263,7 +265,7 @@ struct DecodedCallbackEntry { std::vector indexes; }; bool collect_callback_entries(pb_istream_t *stream, const pb_field_iter_t *, - void **arg) { + void **arg) { auto *entries = static_cast *>(*arg); debug_CallbackEntry entry = debug_CallbackEntry_init_zero; std::vector topic; @@ -278,7 +280,7 @@ bool collect_callback_entries(pb_istream_t *stream, const pb_field_iter_t *, } std::optional find_imported_function(Module *m, - const std::string &name) { + const std::string &name) { for (uint32_t index = 0; index < m->import_count; ++index) { if (m->functions[index].import_field != nullptr && name == m->functions[index].import_field) @@ -318,8 +320,8 @@ bool apply_value_update(const debug_Value &from, StackValue *to) { } } -[[maybe_unused]] void value_to_proto(const StackValue &from, const uint32_t index, - debug_Value *to) { +[[maybe_unused]] void value_to_proto(const StackValue &from, + const uint32_t index, debug_Value *to) { *to = debug_Value_init_zero; to->index = index; switch (from.value_type) { @@ -388,7 +390,7 @@ ValueView current_locals(const ExecutionContext *context) { } bool encode_value(pb_ostream_t *stream, const pb_field_t *field, - const StackValue &source, const size_t index) { + const StackValue &source, const size_t index) { debug_Value value = debug_Value_init_zero; value_to_proto(source, static_cast(index), &value); return pb_encode_tag_for_field(stream, field) && @@ -396,7 +398,7 @@ bool encode_value(pb_ostream_t *stream, const pb_field_t *field, } bool encode_value_range(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *view = static_cast(*arg); for (size_t index = 0; index < view->size; ++index) { const StackValue *value = view->globals == nullptr @@ -408,7 +410,7 @@ bool encode_value_range(pb_ostream_t *stream, const pb_field_t *field, } bool encode_values(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *values = static_cast *>(*arg); ValueView view{values->data(), values->size(), nullptr}; void *range = &view; @@ -416,7 +418,7 @@ bool encode_values(pb_ostream_t *stream, const pb_field_t *field, } bool encode_bytes(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *bytes = static_cast *>(*arg); ByteView view{bytes->data(), bytes->size()}; void *opaque = &view; @@ -424,7 +426,7 @@ bool encode_bytes(pb_ostream_t *stream, const pb_field_t *field, } bool encode_breakpoints(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *view = static_cast(*arg); for (uint8_t *breakpoint : view->debugger->breakpoints) { const uint32_t address = toVirtualAddress(breakpoint, view->module); @@ -436,7 +438,7 @@ bool encode_breakpoints(pb_ostream_t *stream, const pb_field_t *field, } bool encode_functions(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *view = static_cast(*arg); Module *module = view->module; @@ -468,7 +470,7 @@ bool encode_functions(pb_ostream_t *stream, const pb_field_t *field, } bool encode_callstack(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *view = static_cast(*arg); const ExecutionContext *context = view->context; @@ -496,7 +498,7 @@ bool encode_callstack(pb_ostream_t *stream, const pb_field_t *field, } bool encode_callback_indexes(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *callbacks = static_cast *>(*arg); for (const Callback &callback : *callbacks) { if (!pb_encode_tag_for_field(stream, field) || @@ -508,7 +510,7 @@ bool encode_callback_indexes(pb_ostream_t *stream, const pb_field_t *field, } bool encode_callbacks(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *callbacks = static_cast(*arg); @@ -531,7 +533,7 @@ bool encode_callbacks(pb_ostream_t *stream, const pb_field_t *field, } bool encode_events(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *range = static_cast(*arg); for (size_t index = 0; index < range->size; ++index) { @@ -558,7 +560,7 @@ bool encode_events(pb_ostream_t *stream, const pb_field_t *field, } bool encode_io_state(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *states = static_cast *>(*arg); for (const IOStateElement *source : *states) { @@ -578,7 +580,7 @@ bool encode_io_state(pb_ostream_t *stream, const pb_field_t *field, } bool encode_overrides(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { + void *const *arg) { const auto *view = static_cast(*arg); for (const auto &[key, result] : *view->overrides) { @@ -656,7 +658,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_REMOVE_BREAKPOINT: { debug_Breakpoint breakpoint = debug_Breakpoint_init_zero; if (!decode_payload(message->payload, debug_Breakpoint_fields, - &breakpoint) || + &breakpoint) || !breakpoint.has_location || breakpoint.location.module_index != 0 || !isToPhysicalAddrPossible(breakpoint.location.program_counter, @@ -676,7 +678,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_CONTINUE_FOR: { debug_ContinueFor request = debug_ContinueFor_init_zero; if (!decode_payload(message->payload, debug_ContinueFor_fields, - &request) || + &request) || request.count == 0) { malformed(); break; @@ -689,14 +691,14 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_DUMP: if (!require_empty()) break; pause_runtime(m); - encode_snapshot(m, - snapshotPc | snapshotBreakpoints | - snapshotCallstack | snapshotGlobals | - snapshotTable | snapshotBranchTable | - snapshotStack | snapshotCallbacks | - snapshotEvents | snapshotIO | snapshotOverrides | - snapshotHeap | snapshotLocals, - debug_NotificationType_NOTIFICATION_SNAPSHOT); + encode_snapshot( + m, + snapshotPc | snapshotBreakpoints | snapshotCallstack | + snapshotGlobals | snapshotTable | snapshotBranchTable | + snapshotStack | snapshotCallbacks | snapshotEvents | + snapshotIO | snapshotOverrides | snapshotHeap | + snapshotLocals, + debug_NotificationType_NOTIFICATION_SNAPSHOT); break; case debug_Command_COMMAND_DUMP_LOCALS: if (!require_empty()) break; @@ -725,13 +727,12 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_UPDATE_LOCAL: { const auto update = update_value(message->payload); ExecutionContext *context = m->warduino->execution_context; - if (!update || context->fp + static_cast(update->index) > - context->sp) { + if (!update || + context->fp + static_cast(update->index) > context->sp) { malformed(); break; } - StackValue *value = - &context->stack[context->fp + update->index]; + StackValue *value = &context->stack[context->fp + update->index]; if (!apply_value_update(update->value, value)) { malformed(); break; @@ -756,8 +757,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_UPDATE_STACK: { const auto update = update_value(message->payload); ExecutionContext *context = m->warduino->execution_context; - if (!update || - update->index > static_cast(context->sp)) { + if (!update || update->index > static_cast(context->sp)) { malformed(); break; } @@ -774,7 +774,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { std::vector wasm; set_decode_callback(&update.wasm, &wasm); if (!decode_payload(message->payload, debug_ModuleUpdate_fields, - &update) || + &update) || wasm.empty()) { malformed(); break; @@ -794,7 +794,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { std::vector instructions; set_decode_callback(&update.instructions, &instructions); if (!decode_payload(message->payload, debug_Function_fields, - &update) || + &update) || update.function_index >= m->function_count || instructions.empty() || instructions.back() != 0x0b) { malformed(); @@ -815,7 +815,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { mapping.entries.funcs.decode = collect_callback_entries; mapping.entries.arg = &entries; if (!decode_payload(message->payload, debug_CallbackMapping_fields, - &mapping)) { + &mapping)) { malformed(); break; } @@ -835,11 +835,11 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { set_decode_callback(&config.selected_state, &selectedState); SnapshotSelection selectedMask = 0; if (!decode_payload(message->payload, - debug_SnapshotPolicyConfig_fields, &config) || + debug_SnapshotPolicyConfig_fields, &config) || config.policy > debug_SnapshotPolicy_SNAPSHOT_POLICY_CHECKPOINTING || !parse_selection(selectedState.data(), selectedState.size(), - &selectedMask)) { + &selectedMask)) { malformed(); break; } @@ -873,7 +873,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { request.argument_words.funcs.decode = collect_words; request.argument_words.arg = &words; if (!decode_payload(message->payload, debug_Override_fields, - &request)) { + &request)) { malformed(); break; } @@ -899,24 +899,25 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { std::vector selected; set_decode_callback(&request.state, &selected); if (!decode_payload(message->payload, debug_Inspect_fields, - &request)) { + &request)) { malformed(); break; } SnapshotSelection selection = 0; - if (!parse_selection(selected.data(), selected.size(), &selection)) { + if (!parse_selection(selected.data(), selected.size(), + &selection)) { malformed(); break; } pause_runtime(m); encode_snapshot(m, selection, - debug_NotificationType_NOTIFICATION_SNAPSHOT); + debug_NotificationType_NOTIFICATION_SNAPSHOT); break; } case debug_Command_COMMAND_LOAD_SNAPSHOT: { debug_Snapshot state = debug_Snapshot_init_zero; if (!decode_payload(message->payload, debug_Snapshot_fields, - &state) || + &state) || !isToPhysicalAddrPossible(state.program_counter, m)) { malformed(); break; @@ -931,7 +932,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_REMOVE_PROXY: { debug_FunctionRef reference = debug_FunctionRef_init_zero; if (!decode_payload(message->payload, debug_FunctionRef_fields, - &reference) || + &reference) || supervisor == nullptr || reference.function_index >= m->function_count) { send_operation_result(message->type, false); @@ -951,7 +952,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { call.arguments.funcs.decode = collect_values; call.arguments.arg = &values; if (!decode_payload(message->payload, - debug_RemoteFunctionCall_fields, &call) || + debug_RemoteFunctionCall_fields, &call) || call.function_index >= m->function_count || values.size() != m->functions[call.function_index].type->param_count) { @@ -961,7 +962,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { auto *arguments = new StackValue[values.size()]; bool valid = true; for (size_t index = 0; index < values.size(); ++index) - valid &= value_from_proto(values[index], &arguments[index]); + valid &= apply_value_update(values[index], &arguments[index]); if (!valid) { delete[] arguments; malformed(); @@ -1014,7 +1015,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { case debug_Command_COMMAND_POP_EVENT: if (!require_empty()) break; send_operation_result(message->type, - CallbackHandler::resolve_event(true)); + CallbackHandler::resolve_event(true)); break; case debug_Command_COMMAND_PUSH_EVENT: { debug_Event event = debug_Event_init_zero; @@ -1046,7 +1047,7 @@ bool Debugger::check_debug_messages(Module *m, RunningState *program_state) { // Private methods void Debugger::print_value(const StackValue *, const uint32_t, - const bool) const {} + const bool) const {} uint8_t *Debugger::find_opcode(Module *m, const Block *block) { const auto find = @@ -1086,7 +1087,7 @@ void Debugger::handle_invoke(Module *m, uint8_t *interruptData) const { } void Debugger::handle_interrupt_run(const Module *m, - RunningState *program_state) { + RunningState *program_state) { ExecutionContext *ectx = m->warduino->execution_context; if (*program_state == WARDUINOpause && this->is_breakpoint(ectx->pc_ptr)) { this->skipBreakpoint = ectx->pc_ptr; @@ -1158,7 +1159,7 @@ void Debugger::dump_stack(const Module *m) const { locals.values.funcs.encode = encode_value_range; locals.values.arg = &values; send_notification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, - debug_Locals_fields, &locals); + debug_Locals_fields, &locals); } void Debugger::dump_breakpoints(Module *) const {} @@ -1176,7 +1177,7 @@ void Debugger::dump_locals(const Module *m) const { locals.values.funcs.encode = encode_value_range; locals.values.arg = &values; send_notification(debug_NotificationType_NOTIFICATION_LOCALS_DUMP, - debug_Locals_fields, &locals); + debug_Locals_fields, &locals); } void Debugger::dump_events(long start, long size) const { @@ -1194,7 +1195,7 @@ void Debugger::dump_events(long start, long size) const { queue.events.funcs.encode = encode_events; queue.events.arg = ⦥ send_notification(debug_NotificationType_NOTIFICATION_EVENTS_DUMP, - debug_EventsQueue_fields, &queue); + debug_EventsQueue_fields, &queue); } void Debugger::dump_callback_mapping() const { @@ -1204,7 +1205,7 @@ void Debugger::dump_callback_mapping() const { mapping.entries.arg = const_cast(&callbacks); send_notification(debug_NotificationType_NOTIFICATION_CALLBACKS_DUMP, - debug_CallbackMapping_fields, &mapping); + debug_CallbackMapping_fields, &mapping); } void Debugger::dump_heap_info(Module *) const {} @@ -1310,7 +1311,7 @@ void Debugger::notify_pushed_event() const { bool Debugger::handle_pushed_event(char *) const { return false; } bool Debugger::parse_selection(const uint8_t *state, const size_t size, - SnapshotSelection *selection) { + SnapshotSelection *selection) { *selection = 0; for (size_t index = 0; index < size; ++index) { if (state[index] < pcState || state[index] > heapState) return false; @@ -1319,8 +1320,9 @@ bool Debugger::parse_selection(const uint8_t *state, const size_t size, return true; } -bool Debugger::encode_snapshot(Module *m, const SnapshotSelection selection, - const debug_NotificationType notification) const { +bool Debugger::encode_snapshot( + Module *m, const SnapshotSelection selection, + const debug_NotificationType notification) const { ExecutionContext *ectx = m->warduino->execution_context; SnapshotView view{m, this, ectx, &overrides}; debug_Snapshot state = debug_Snapshot_init_zero; @@ -1512,9 +1514,10 @@ void Debugger::handle_snapshot_policy(Module *m) { if (snapshotPolicy == SnapshotPolicy::atEveryInstruction) { SnapshotSelection selection = 0; if (checkpoint_state != nullptr && - parse_selection(checkpoint_state, checkpoint_state_size, &selection)) + parse_selection(checkpoint_state, checkpoint_state_size, + &selection)) encode_snapshot(m, selection, - debug_NotificationType_NOTIFICATION_SNAPSHOT); + debug_NotificationType_NOTIFICATION_SNAPSHOT); } else if (snapshotPolicy == SnapshotPolicy::checkpointing) { if (instructions_executed >= checkpointInterval || fidx_called) { if (min_return_values == 0) { @@ -1590,7 +1593,7 @@ void Debugger::checkpoint(Module *m, const bool force) { notification.snapshot.heap_used = m->warduino->get_heap_used(); } send_notification(debug_NotificationType_NOTIFICATION_CHECKPOINT, - debug_Checkpoint_fields, ¬ification); + debug_Checkpoint_fields, ¬ification); instructions_executed = 0; } @@ -1991,7 +1994,7 @@ void Debugger::proxify() { } void Debugger::handle_proxy_call(Module *m, RunningState *, - uint8_t *interruptData) const { + uint8_t *interruptData) const { if (this->proxy == nullptr) { dbg_info("No proxy available to send proxy call to.\n"); // TODO how to handle this error? @@ -2022,8 +2025,9 @@ void Debugger::send_proxy_call_result(Module *m) const { if (rfc == nullptr) return; debug_RemoteFunctionResult result = debug_RemoteFunctionResult_init_zero; result.success = rfc->success; - send_notification(debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, - debug_RemoteFunctionResult_fields, &result); + send_notification( + debug_NotificationType_NOTIFICATION_REMOTE_FUNCTION_RESULT, + debug_RemoteFunctionResult_fields, &result); delete rfc; } @@ -2034,7 +2038,7 @@ bool Debugger::is_proxied(const uint32_t fidx) const { } void Debugger::handle_monitor_proxies(const Module *m, - uint8_t *interruptData) const { + uint8_t *interruptData) const { const uint32_t amount_funcs = read_B32(&interruptData); printf("funcs_total %" PRIu32 "\n", amount_funcs); @@ -2093,7 +2097,8 @@ bool Debugger::handle_update_module(Module *m, uint8_t *data) { return true; } -bool Debugger::handle_update_global_value(const Module *m, uint8_t *data) const { +bool Debugger::handle_update_global_value(const Module *m, + uint8_t *data) const { debug("Global updates: %x\n", *data); const uint32_t index = read_LEB_32(&data); @@ -2107,7 +2112,8 @@ bool Debugger::handle_update_global_value(const Module *m, uint8_t *data) const return true; } -bool Debugger::handle_update_stack_value(const Module *m, uint8_t *bytes) const { +bool Debugger::handle_update_stack_value(const Module *m, + uint8_t *bytes) const { const uint32_t idx = read_LEB_32(&bytes); if (idx >= STACK_SIZE) { return false; diff --git a/src/Debug/debugger.h b/src/Debug/debugger.h index 52c1cacf6..0716a2e0e 100644 --- a/src/Debug/debugger.h +++ b/src/Debug/debugger.h @@ -197,8 +197,8 @@ class Debugger { void push_message(DebugMessage msg); bool send_notification(debug_NotificationType type, - const pb_msgdesc_t *fields = nullptr, - const void *payload = nullptr) const; + const pb_msgdesc_t *fields = nullptr, + const void *payload = nullptr) const; void send_operation_result(debug_Command command, bool success) const; //// Handle REPL interrupts @@ -238,9 +238,9 @@ class Debugger { void inspect(Module *m, uint16_t sizeStateArray, const uint8_t *state) const; bool encode_snapshot(Module *m, SnapshotSelection selection, - debug_NotificationType notification) const; + debug_NotificationType notification) const; static bool parse_selection(const uint8_t *state, size_t size, - SnapshotSelection *selection); + SnapshotSelection *selection); //// Handle live code update @@ -339,7 +339,7 @@ class Debugger { void proxify(); void handle_proxy_call(Module *m, RunningState *program_state, - uint8_t *interruptData) const; + uint8_t *interruptData) const; RFC *top_proxy_call() const; diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index 470a18dfd..1f790dcc7 100644 --- a/src/Interpreter/instructions.cpp +++ b/src/Interpreter/instructions.cpp @@ -306,7 +306,8 @@ bool i_instr_call(Module *m) { // Mocking only works on primitives, no need to check for it otherwise. if (ectx->sp >= 0) { uint32_t mock_result; - if (m->warduino->debugger->get_mock_for_args(m, fidx, mock_result)) { + if (m->warduino->debugger->get_mock_for_args(m, fidx, + mock_result)) { const uint32_t param_count = m->functions[fidx].type->param_count; ectx->sp -= static_cast(param_count) - 1; diff --git a/tests/compilation/esp32/esp32.ino b/tests/compilation/esp32/esp32.ino index cd94cb899..eae4b88f5 100644 --- a/tests/compilation/esp32/esp32.ino +++ b/tests/compilation/esp32/esp32.ino @@ -23,7 +23,7 @@ Module* m; void startDebuggerStd(void* pvParameter) { Channel* sink = new Sink(stdout); - wac->debugger->setChannel(sink); + wac->debugger->set_channel(sink); sink->open(); uint8_t buffer[1024] = {0}; diff --git a/tutorials/wat/main/main.cpp b/tutorials/wat/main/main.cpp index 6acd43b87..81965c793 100644 --- a/tutorials/wat/main/main.cpp +++ b/tutorials/wat/main/main.cpp @@ -26,7 +26,7 @@ Module* m; void startDebuggerStd(void* pvParameter) { Channel* duplex = new Duplex(stdin, stdout); - wac->debugger->setChannel(duplex); + wac->debugger->set_channel(duplex); duplex->open(); int valread;