Repository: eclipse-zenoh/zenoh-pico Branch: main Commit: 7a2e422e7cb3 Files: 637 Total size: 4.2 MB Directory structure: gitextract_8cd1ym4i/ ├── .clang-format ├── .cmake-format.py ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ └── release.yml │ ├── pull_request_template.md │ ├── release.yml │ └── workflows/ │ ├── arduino_esp32.yaml │ ├── build-shared.yaml │ ├── build-static.yaml │ ├── ci.yml │ ├── codacy-analysis.yml │ ├── cpp-check.yaml │ ├── emscripten.yaml │ ├── espidf.yaml │ ├── freertos_plus_tcp.yaml │ ├── integration.yaml │ ├── mbed.yaml │ ├── pr-label-checklists-generate.yml │ ├── pr-label-checklists-verify.yml │ ├── release.yml │ ├── rpi_pico.yaml │ ├── update-release-project.yml │ └── zephyr.yaml ├── .gitignore ├── .markdownlint.yaml ├── .readthedocs.yaml ├── BSDmakefile ├── CMakeLists.txt ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── GNUmakefile ├── LICENSE ├── NOTICE.md ├── PackageConfig.cmake.in ├── README.md ├── ci/ │ └── scripts/ │ ├── build-linux.bash │ ├── build-macos.bash │ ├── bump-and-tag.bash │ └── pre-build.bash ├── cmake/ │ ├── helpers.cmake │ ├── platformConfig.cmake │ ├── platforms/ │ │ ├── arduino_esp32.cmake │ │ ├── bsd.cmake │ │ ├── emscripten.cmake │ │ ├── espidf.cmake │ │ ├── flipper.cmake │ │ ├── freertos_lwip.cmake │ │ ├── freertos_plus_tcp.cmake │ │ ├── linux.cmake │ │ ├── macos.cmake │ │ ├── mbed.cmake │ │ ├── opencr.cmake │ │ ├── posix_compatible.cmake │ │ ├── rpi_pico.cmake │ │ ├── threadx_stm32.cmake │ │ ├── windows.cmake │ │ └── zephyr.cmake │ └── platforms.cmake ├── colcon.pkg ├── docs/ │ ├── .gitignore │ ├── api.rst │ ├── concepts.rst │ ├── conf.py │ ├── config.rst │ ├── index.rst │ ├── platforms.rst │ ├── requirements.txt │ └── zephyr/ │ ├── frdm_mcxn947/ │ │ └── prj.conf │ ├── nRF52840/ │ │ ├── CMakeLists.txt │ │ ├── platformio.ini │ │ └── prj.conf │ ├── nucleo_f767zi/ │ │ ├── CMakeLists.txt │ │ ├── platformio.ini │ │ └── prj.conf │ └── reel_board/ │ ├── CMakeLists.txt │ ├── platformio.ini │ └── prj.conf ├── examples/ │ ├── CMakeLists.txt │ ├── arduino/ │ │ ├── z_get.ino │ │ ├── z_pub.ino │ │ ├── z_pull.ino │ │ ├── z_queryable.ino │ │ ├── z_scout.ino │ │ └── z_sub.ino │ ├── espidf/ │ │ ├── z_get.c │ │ ├── z_pub.c │ │ ├── z_pull.c │ │ ├── z_queryable.c │ │ ├── z_scout.c │ │ └── z_sub.c │ ├── freertos_plus_tcp/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── FreeRTOSConfig.h │ │ │ └── FreeRTOSIPConfig.h │ │ ├── main.c │ │ ├── z_get.c │ │ ├── z_pub.c │ │ ├── z_pub_st.c │ │ ├── z_pull.c │ │ ├── z_put.c │ │ ├── z_queryable.c │ │ ├── z_scout.c │ │ ├── z_sub.c │ │ └── z_sub_st.c │ ├── mbed/ │ │ ├── z_get.cpp │ │ ├── z_pub.cpp │ │ ├── z_pull.cpp │ │ ├── z_queryable.cpp │ │ ├── z_scout.cpp │ │ └── z_sub.cpp │ ├── packages/ │ │ └── zenohpico-mylinux/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── cmake/ │ │ │ ├── platforms/ │ │ │ │ └── mylinux.cmake │ │ │ └── zenohpico-mylinuxConfig.cmake │ │ ├── consumer/ │ │ │ ├── CMakeLists.txt │ │ │ └── main.c │ │ └── include/ │ │ └── zenoh_mylinux_platform.h │ ├── platforms/ │ │ └── myplatform.cmake │ ├── rpi_pico/ │ │ ├── CMakeLists.txt │ │ ├── FreeRTOS_Kernel_import.cmake │ │ ├── include/ │ │ │ ├── FreeRTOSConfig.h │ │ │ ├── config.h.in │ │ │ ├── lwipopts.h │ │ │ └── tusb/ │ │ │ └── tusb_config.h │ │ ├── main.c │ │ ├── z_get.c │ │ ├── z_pub.c │ │ ├── z_pub_st.c │ │ ├── z_pub_thr.c │ │ ├── z_pull.c │ │ ├── z_put.c │ │ ├── z_queryable.c │ │ ├── z_scout.c │ │ ├── z_sub.c │ │ ├── z_sub_st.c │ │ └── z_sub_thr.c │ ├── threadx_stm32/ │ │ ├── z_pub.c │ │ └── z_sub.c │ ├── unix/ │ │ ├── c11/ │ │ │ ├── z_advanced_pub.c │ │ │ ├── z_advanced_sub.c │ │ │ ├── z_bytes.c │ │ │ ├── z_get.c │ │ │ ├── z_get_attachment.c │ │ │ ├── z_get_channel.c │ │ │ ├── z_get_lat.c │ │ │ ├── z_get_liveliness.c │ │ │ ├── z_info.c │ │ │ ├── z_liveliness.c │ │ │ ├── z_ping.c │ │ │ ├── z_pong.c │ │ │ ├── z_pub.c │ │ │ ├── z_pub_attachment.c │ │ │ ├── z_pub_st.c │ │ │ ├── z_pub_thr.c │ │ │ ├── z_pub_tls.c │ │ │ ├── z_pull.c │ │ │ ├── z_put.c │ │ │ ├── z_querier.c │ │ │ ├── z_queryable.c │ │ │ ├── z_queryable_attachment.c │ │ │ ├── z_queryable_channel.c │ │ │ ├── z_queryable_lat.c │ │ │ ├── z_scout.c │ │ │ ├── z_sub.c │ │ │ ├── z_sub_attachment.c │ │ │ ├── z_sub_channel.c │ │ │ ├── z_sub_liveliness.c │ │ │ ├── z_sub_st.c │ │ │ ├── z_sub_thr.c │ │ │ └── z_sub_tls.c │ │ └── c99/ │ │ ├── z_get.c │ │ ├── z_info.c │ │ ├── z_ping.c │ │ ├── z_pong.c │ │ ├── z_pub.c │ │ ├── z_pub_st.c │ │ ├── z_pull.c │ │ ├── z_put.c │ │ ├── z_queryable.c │ │ ├── z_scout.c │ │ ├── z_sub.c │ │ └── z_sub_st.c │ ├── windows/ │ │ ├── z_get.c │ │ ├── z_info.c │ │ ├── z_ping.c │ │ ├── z_pong.c │ │ ├── z_pub.c │ │ ├── z_pub_st.c │ │ ├── z_pull.c │ │ ├── z_put.c │ │ ├── z_queryable.c │ │ ├── z_scout.c │ │ ├── z_sub.c │ │ └── z_sub_st.c │ └── zephyr/ │ ├── z_get.c │ ├── z_pub.c │ ├── z_pull.c │ ├── z_queryable.c │ ├── z_scout.c │ └── z_sub.c ├── extra_script.py ├── include/ │ ├── zenoh-pico/ │ │ ├── api/ │ │ │ ├── admin_space.h │ │ │ ├── advanced_publisher.h │ │ │ ├── advanced_subscriber.h │ │ │ ├── constants.h │ │ │ ├── encoding.h │ │ │ ├── handlers.h │ │ │ ├── liveliness.h │ │ │ ├── macros.h │ │ │ ├── olv_macros.h │ │ │ ├── primitives.h │ │ │ ├── serialization.h │ │ │ └── types.h │ │ ├── collections/ │ │ │ ├── advanced_cache.h │ │ │ ├── arc_slice.h │ │ │ ├── array.h │ │ │ ├── atomic.h │ │ │ ├── bytes.h │ │ │ ├── cat.h │ │ │ ├── deque_template.h │ │ │ ├── element.h │ │ │ ├── fifo.h │ │ │ ├── fifo_mt.h │ │ │ ├── hashmap.h │ │ │ ├── hashmap_template.h │ │ │ ├── intmap.h │ │ │ ├── lifo.h │ │ │ ├── list.h │ │ │ ├── lru_cache.h │ │ │ ├── pqueue_template.h │ │ │ ├── refcount.h │ │ │ ├── ring.h │ │ │ ├── ring_mt.h │ │ │ ├── seqnumber.h │ │ │ ├── slice.h │ │ │ ├── sortedmap.h │ │ │ ├── string.h │ │ │ ├── sync_group.h │ │ │ └── vec.h │ │ ├── config.h.in │ │ ├── link/ │ │ │ ├── config/ │ │ │ │ ├── bt.h │ │ │ │ ├── raweth.h │ │ │ │ ├── serial.h │ │ │ │ ├── tcp.h │ │ │ │ ├── tls.h │ │ │ │ ├── udp.h │ │ │ │ └── ws.h │ │ │ ├── endpoint.h │ │ │ ├── link.h │ │ │ ├── manager.h │ │ │ └── transport/ │ │ │ ├── bt.h │ │ │ ├── lwip_socket.h │ │ │ ├── raweth.h │ │ │ ├── serial.h │ │ │ ├── serial_protocol.h │ │ │ ├── socket.h │ │ │ ├── tcp.h │ │ │ ├── tls_stream.h │ │ │ ├── udp_multicast.h │ │ │ ├── udp_unicast.h │ │ │ └── ws.h │ │ ├── net/ │ │ │ ├── config.h │ │ │ ├── encoding.h │ │ │ ├── filtering.h │ │ │ ├── liveliness.h │ │ │ ├── logger.h │ │ │ ├── matching.h │ │ │ ├── primitives.h │ │ │ ├── publish.h │ │ │ ├── query.h │ │ │ ├── reply.h │ │ │ ├── sample.h │ │ │ ├── session.h │ │ │ ├── subscribe.h │ │ │ └── zenoh-pico.h │ │ ├── protocol/ │ │ │ ├── codec/ │ │ │ │ ├── core.h │ │ │ │ ├── declarations.h │ │ │ │ ├── ext.h │ │ │ │ ├── interest.h │ │ │ │ ├── message.h │ │ │ │ ├── network.h │ │ │ │ ├── serial.h │ │ │ │ └── transport.h │ │ │ ├── codec.h │ │ │ ├── core.h │ │ │ ├── definitions/ │ │ │ │ ├── core.h │ │ │ │ ├── declarations.h │ │ │ │ ├── interest.h │ │ │ │ ├── message.h │ │ │ │ ├── network.h │ │ │ │ ├── serial.h │ │ │ │ └── transport.h │ │ │ ├── ext.h │ │ │ └── iobuf.h │ │ ├── runtime/ │ │ │ ├── background_executor.h │ │ │ ├── executor.h │ │ │ └── runtime.h │ │ ├── session/ │ │ │ ├── cancellation.h │ │ │ ├── interest.h │ │ │ ├── keyexpr.h │ │ │ ├── keyexpr_match_template.h │ │ │ ├── liveliness.h │ │ │ ├── loopback.h │ │ │ ├── matching.h │ │ │ ├── push.h │ │ │ ├── query.h │ │ │ ├── queryable.h │ │ │ ├── reply.h │ │ │ ├── resource.h │ │ │ ├── session.h │ │ │ ├── subscription.h │ │ │ ├── utils.h │ │ │ └── weak_session.h │ │ ├── system/ │ │ │ ├── common/ │ │ │ │ ├── platform.h │ │ │ │ └── system_error.h │ │ │ ├── platform/ │ │ │ │ ├── arduino/ │ │ │ │ │ ├── esp32.h │ │ │ │ │ └── opencr.h │ │ │ │ ├── emscripten.h │ │ │ │ ├── espidf.h │ │ │ │ ├── flipper.h │ │ │ │ ├── freertos/ │ │ │ │ │ ├── freertos_plus_tcp.h │ │ │ │ │ └── lwip.h │ │ │ │ ├── mbed.h │ │ │ │ ├── rpi_pico.h │ │ │ │ ├── threadx/ │ │ │ │ │ └── stm32.h │ │ │ │ ├── unix.h │ │ │ │ ├── void.h │ │ │ │ ├── windows.h │ │ │ │ └── zephyr.h │ │ │ └── platform.h │ │ ├── transport/ │ │ │ ├── common/ │ │ │ │ ├── lease.h │ │ │ │ ├── read.h │ │ │ │ ├── rx.h │ │ │ │ ├── transport.h │ │ │ │ └── tx.h │ │ │ ├── manager.h │ │ │ ├── multicast/ │ │ │ │ ├── lease.h │ │ │ │ ├── read.h │ │ │ │ ├── rx.h │ │ │ │ └── transport.h │ │ │ ├── multicast.h │ │ │ ├── raweth/ │ │ │ │ ├── read.h │ │ │ │ ├── rx.h │ │ │ │ └── tx.h │ │ │ ├── transport.h │ │ │ ├── unicast/ │ │ │ │ ├── accept.h │ │ │ │ ├── lease.h │ │ │ │ ├── read.h │ │ │ │ ├── rx.h │ │ │ │ └── transport.h │ │ │ ├── unicast.h │ │ │ └── utils.h │ │ └── utils/ │ │ ├── checksum.h │ │ ├── config.h │ │ ├── encoding.h │ │ ├── endianness.h │ │ ├── hash.h │ │ ├── json_encoder.h │ │ ├── locality.h │ │ ├── logging.h │ │ ├── mutex.h │ │ ├── pointers.h │ │ ├── query_params.h │ │ ├── result.h │ │ ├── sleep.h │ │ ├── string.h │ │ ├── time_range.h │ │ └── uuid.h │ ├── zenoh-pico.h │ └── zenoh-pico.h.in ├── library.json ├── src/ │ ├── api/ │ │ ├── admin_space.c │ │ ├── advanced_publisher.c │ │ ├── advanced_subscriber.c │ │ ├── api.c │ │ ├── connectivity.c │ │ ├── encoding.c │ │ ├── liveliness.c │ │ └── serialization.c │ ├── collections/ │ │ ├── advanced_cache.c │ │ ├── arc_slice.c │ │ ├── atomic.c │ │ ├── bytes.c │ │ ├── fifo.c │ │ ├── fifo_mt.c │ │ ├── hashmap.c │ │ ├── lifo.c │ │ ├── list.c │ │ ├── lru_cache.c │ │ ├── refcount.c │ │ ├── ring.c │ │ ├── ring_mt.c │ │ ├── slice.c │ │ ├── sortedmap.c │ │ ├── string.c │ │ ├── sync_group.c │ │ └── vec.c │ ├── link/ │ │ ├── config/ │ │ │ ├── bt.c │ │ │ ├── serial.c │ │ │ ├── tcp.c │ │ │ ├── tls.c │ │ │ ├── udp.c │ │ │ └── ws.c │ │ ├── endpoint.c │ │ ├── link.c │ │ ├── multicast/ │ │ │ ├── bt.c │ │ │ └── udp.c │ │ ├── transport/ │ │ │ ├── bt/ │ │ │ │ └── bt_arduino_esp32.cpp │ │ │ ├── common/ │ │ │ │ ├── address.c │ │ │ │ └── endpoints.c │ │ │ ├── serial/ │ │ │ │ ├── tty_posix.c │ │ │ │ ├── uart_arduino_esp32.cpp │ │ │ │ ├── uart_espidf.c │ │ │ │ ├── uart_flipper.c │ │ │ │ ├── uart_mbed.cpp │ │ │ │ ├── uart_rpi_pico.c │ │ │ │ ├── uart_threadx_stm32.c │ │ │ │ └── uart_zephyr.c │ │ │ ├── tcp/ │ │ │ │ ├── address.c │ │ │ │ ├── tcp_esp32.c │ │ │ │ ├── tcp_freertos_plus_tcp.c │ │ │ │ ├── tcp_lwip.c │ │ │ │ ├── tcp_mbed.cpp │ │ │ │ ├── tcp_opencr.cpp │ │ │ │ ├── tcp_posix.c │ │ │ │ ├── tcp_windows.c │ │ │ │ └── tcp_zephyr.c │ │ │ ├── udp/ │ │ │ │ ├── address.c │ │ │ │ ├── raweth_unix.c │ │ │ │ ├── udp_esp32.c │ │ │ │ ├── udp_freertos_plus_tcp.c │ │ │ │ ├── udp_lwip.c │ │ │ │ ├── udp_mbed.cpp │ │ │ │ ├── udp_multicast_esp32.c │ │ │ │ ├── udp_multicast_lwip.c │ │ │ │ ├── udp_multicast_lwip_common.c │ │ │ │ ├── udp_multicast_lwip_common.h │ │ │ │ ├── udp_multicast_mbed.cpp │ │ │ │ ├── udp_multicast_opencr.cpp │ │ │ │ ├── udp_multicast_posix.c │ │ │ │ ├── udp_multicast_rpi_pico.c │ │ │ │ ├── udp_multicast_windows.c │ │ │ │ ├── udp_multicast_zephyr.c │ │ │ │ ├── udp_opencr.cpp │ │ │ │ ├── udp_posix.c │ │ │ │ ├── udp_windows.c │ │ │ │ └── udp_zephyr.c │ │ │ └── upper/ │ │ │ ├── serial_protocol.c │ │ │ ├── tls_stream.c │ │ │ ├── ws_emscripten.c │ │ │ └── ws_stream.c │ │ └── unicast/ │ │ ├── serial.c │ │ ├── tcp.c │ │ ├── tls.c │ │ ├── udp.c │ │ └── ws.c │ ├── net/ │ │ ├── config.c │ │ ├── encoding.c │ │ ├── filtering.c │ │ ├── liveliness.c │ │ ├── logger.c │ │ ├── matching.c │ │ ├── memory.c │ │ ├── primitives.c │ │ ├── query.c │ │ ├── reply.c │ │ ├── sample.c │ │ ├── session.c │ │ └── subscribe.c │ ├── protocol/ │ │ ├── codec/ │ │ │ ├── core.c │ │ │ ├── declarations.c │ │ │ ├── ext.c │ │ │ ├── interest.c │ │ │ ├── message.c │ │ │ ├── network.c │ │ │ ├── serial.c │ │ │ └── transport.c │ │ ├── codec.c │ │ ├── config.c │ │ ├── core.c │ │ ├── definitions/ │ │ │ ├── declarations.c │ │ │ ├── interest.c │ │ │ ├── message.c │ │ │ ├── network.c │ │ │ └── transport.c │ │ ├── ext.c │ │ └── iobuf.c │ ├── runtime/ │ │ ├── background_executor.c │ │ └── executor.c │ ├── session/ │ │ ├── cancellation.c │ │ ├── interest.c │ │ ├── keyexpr.c │ │ ├── liveliness.c │ │ ├── loopback.c │ │ ├── push.c │ │ ├── query.c │ │ ├── queryable.c │ │ ├── reply.c │ │ ├── resource.c │ │ ├── rx.c │ │ ├── scout.c │ │ ├── subscription.c │ │ └── utils.c │ ├── system/ │ │ ├── arduino/ │ │ │ ├── esp32/ │ │ │ │ └── system.c │ │ │ └── opencr/ │ │ │ ├── network.cpp │ │ │ └── system.c │ │ ├── common/ │ │ │ └── platform.c │ │ ├── emscripten/ │ │ │ ├── network.c │ │ │ └── system.c │ │ ├── espidf/ │ │ │ └── system.c │ │ ├── flipper/ │ │ │ ├── network.c │ │ │ └── system.c │ │ ├── freertos/ │ │ │ ├── freertos_plus_tcp/ │ │ │ │ └── network.c │ │ │ └── system.c │ │ ├── mbed/ │ │ │ ├── network.cpp │ │ │ └── system.cpp │ │ ├── rpi_pico/ │ │ │ ├── system.c │ │ │ └── usb_uart.c │ │ ├── socket/ │ │ │ ├── esp32.c │ │ │ └── lwip.c │ │ ├── threadx/ │ │ │ └── stm32/ │ │ │ ├── network.c │ │ │ └── system.c │ │ ├── unix/ │ │ │ ├── network.c │ │ │ └── system.c │ │ ├── windows/ │ │ │ ├── network.c │ │ │ └── system.c │ │ └── zephyr/ │ │ ├── network.c │ │ └── system.c │ ├── transport/ │ │ ├── common/ │ │ │ ├── lease.c │ │ │ ├── read.c │ │ │ ├── rx.c │ │ │ ├── transport.c │ │ │ └── tx.c │ │ ├── manager.c │ │ ├── multicast/ │ │ │ ├── lease.c │ │ │ ├── read.c │ │ │ ├── rx.c │ │ │ └── transport.c │ │ ├── multicast.c │ │ ├── peer.c │ │ ├── raweth/ │ │ │ ├── link.c │ │ │ ├── read.c │ │ │ ├── rx.c │ │ │ └── tx.c │ │ ├── transport.c │ │ ├── unicast/ │ │ │ ├── accept.c │ │ │ ├── lease.c │ │ │ ├── read.c │ │ │ ├── rx.c │ │ │ └── transport.c │ │ ├── unicast.c │ │ └── utils.c │ └── utils/ │ ├── checksum.c │ ├── encoding.c │ ├── json_encoder.c │ ├── pointers.c │ ├── query_params.c │ ├── string.c │ ├── time_range.c │ └── uuid.c ├── tests/ │ ├── api.sh │ ├── attachment.py │ ├── connection_restore.py │ ├── fragment.py │ ├── memory_leak.py │ ├── modularity.py │ ├── multicast.sh │ ├── no_router.py │ ├── package_mylinux.sh.in │ ├── package_myrtos.sh.in │ ├── raweth.py │ ├── routed.sh │ ├── routed_peer_client.py │ ├── single_thread.py │ ├── tls_verify.sh │ ├── utils/ │ │ ├── assert_helpers.h │ │ ├── tcp_proxy.c │ │ └── tcp_proxy.h │ ├── valgrind.supp │ ├── z_api_admin_space_test.c │ ├── z_api_advanced_pubsub_test.c │ ├── z_api_alignment_test.c │ ├── z_api_batching_test.c │ ├── z_api_bytes_test.c │ ├── z_api_callback_drop_on_undeclare_test.c │ ├── z_api_cancellation_test.c │ ├── z_api_connectivity_test.c │ ├── z_api_double_drop_test.c │ ├── z_api_encoding_test.c │ ├── z_api_liveliness_test.c │ ├── z_api_local_queryable_test.c │ ├── z_api_local_subscriber_test.c │ ├── z_api_matching_test.c │ ├── z_api_null_drop_test.c │ ├── z_api_queryable_test.c │ ├── z_api_source_info_test.c │ ├── z_background_executor_test.c │ ├── z_bytes_test.c │ ├── z_cancellation_token_test.c │ ├── z_channels_test.c │ ├── z_client_test.c │ ├── z_collections_test.c │ ├── z_condvar_wait_until_test.c │ ├── z_data_struct_test.c │ ├── z_endpoint_test.c │ ├── z_executor_test.c │ ├── z_hashmap_test.c │ ├── z_iobuf_test.c │ ├── z_json_encoder_test.c │ ├── z_keyexpr_test.c │ ├── z_local_loopback_test.c │ ├── z_lru_cache_test.c │ ├── z_msgcodec_test.c │ ├── z_multi_pubsub_test.c │ ├── z_multi_queryable_test.c │ ├── z_open_test.c │ ├── z_perf_rx.c │ ├── z_perf_tx.c │ ├── z_pqueue_test.c │ ├── z_refcount_test.c │ ├── z_sync_group_test.c │ ├── z_test_fragment_decode_error_transport_zbuf.c │ ├── z_test_fragment_rx.c │ ├── z_test_fragment_tx.c │ ├── z_test_peer_multicast.c │ ├── z_test_peer_unicast.c │ ├── z_tls_config_test.c │ ├── z_tls_test.c │ ├── z_utils_test.c │ └── z_wildcard_subscription_test.c ├── tools/ │ └── z_keyexpr_canonizer.c ├── version.txt ├── zenohpico.pc.in └── zephyr/ ├── CMakeLists.txt ├── Kconfig.zenoh └── module.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- Language: Cpp BasedOnStyle: Google IndentWidth: 4 ColumnLimit: 120 PointerAlignment: Right ================================================ FILE: .cmake-format.py ================================================ with section("format"): line_width = 120 max_subgroups_hwrap = 4 max_pargs_hwrap = 10 with section("markup"): enable_markup = False ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Report a bug description: | Create a bug report to help us improve Zenoh. title: "[Bug] " labels: ["bug"] body: - type: textarea id: summary attributes: label: "Describe the bug" description: | A clear and concise description of the expected behaviour and what the bug is. placeholder: | E.g. zenoh peers can not automatically establish a connection. validations: required: true - type: textarea id: reproduce attributes: label: To reproduce description: "Steps to reproduce the behavior:" placeholder: | 1. Start a subscriber "..." 2. Start a publisher "...." 3. See error validations: required: true - type: textarea id: system attributes: label: System info description: "Please complete the following information:" placeholder: | - Platform: [e.g. Ubuntu 20.04 64-bit] - CPU [e.g. AMD Ryzen 3800X] - Zenoh version/commit [e.g. 6f172ea985d42d20d423a192a2d0d46bb0ce0d11] validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Ask a question url: https://github.com/eclipse-zenoh/roadmap/discussions/categories/zenoh about: Open to the Zenoh community. Share your feedback with the Zenoh team. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Request a feature description: | Suggest a new feature specific to this repository. NOTE: for generic Zenoh ideas use "Ask a question". body: - type: markdown attributes: value: | **Guidelines for a good issue** *Is your feature request related to a problem?* A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] *Describe the solution you'd like* A clear and concise description of what you want to happen. *Describe alternatives you've considered* A clear and concise description of any alternative solutions or features you've considered. *Additional context* Add any other context about the feature request here. - type: textarea id: feature attributes: label: "Describe the feature" validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/release.yml ================================================ name: Add an issue to the next release description: | Add an issue as part of next release. This will be added to the current release project. You must be a contributor to use this template. labels: ["release"] body: - type: markdown attributes: value: | **Guidelines for a good issue** *Is your release item related to a problem?* A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] *Describe the solution you'd like* A clear and concise description of what you want to happen. *Describe alternatives you've considered* A clear and concise description of any alternative solutions or features you've considered. *Additional context* Add any other context about the release item request here. - type: textarea id: item attributes: label: "Describe the release item" validations: required: true ================================================ FILE: .github/pull_request_template.md ================================================ ## Description **⚠️ Please replace this section with your PR description** ### What does this PR do? ### Why is this change needed? ### Related Issues ================================================ FILE: .github/release.yml ================================================ # # Copyright (c) 2023 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # changelog: categories: - title: Breaking changes 💥 labels: - breaking-change - title: New features 🎉 labels: - enhancement - new feature exclude: labels: - internal - title: Bug fixes 🐞 labels: - bug exclude: labels: - internal - title: Documentation 📝 labels: - documentation exclude: labels: - internal - title: Dependencies 👷 labels: - dependencies exclude: labels: - internal - title: Other changes labels: - "*" exclude: labels: - internal ================================================ FILE: .github/workflows/arduino_esp32.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: arduino_esp32 on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.platformio/.cache key: ${{ runner.os }}-pio - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install PlatformIO Core run: pip install --upgrade platformio - name: Set up project run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ mkdir -p $ARDUINO_BASE cd $ARDUINO_BASE pio init -b esp32thing_plus -O "-DZ_FEATURE_LINK_BLUETOOTH=1 -DZ_FEATURE_LINK_SERIAL=1" -O "build_flags=-DZENOH_COMPILER_GCC -DZENOH_LOG_DEBUG" -O "lib_ldf_mode=deep+" cd $ARDUINO_BASE/lib ln -s $ZENOH_PICO_BASE cd $ARDUINO_BASE - name: Build z_pub example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_pub.ino cd $ARDUINO_BASE pio run - name: Build z_sub example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_sub.ino cd $ARDUINO_BASE pio run - name: Build z_pull example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_pull.ino cd $ARDUINO_BASE pio run - name: Build z_get example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_get.ino cd $ARDUINO_BASE pio run - name: Build z_queryable example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_queryable.ino cd $ARDUINO_BASE pio run - name: Build z_scout example run: | cd $HOME export ARDUINO_BASE=$HOME/work/arduino_esp32project/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ARDUINO_BASE/src/* cd $ARDUINO_BASE/src ln -s $ZENOH_PICO_BASE/examples/arduino/z_scout.ino cd $ARDUINO_BASE pio run ================================================ FILE: .github/workflows/build-shared.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: build-shared on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v4 - name: Compile debug run: make all env: BUILD_TYPE: Debug BUILD_SHARED_LIBS: ON BUILD_TESTING: ON BUILD_INTEGRATION: ON BUILD_TOOLS: ON ZENOH_LOG: debug crossbuilds: name: Build on ubuntu-latest runs-on: ubuntu-latest steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: tool-cache: false android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: true - uses: actions/checkout@v4 - name: Crosscompile debug run: make crossbuilds env: BUILD_TYPE: Debug BUILD_SHARED_LIBS: ON ZENOH_LOG: debug ================================================ FILE: .github/workflows/build-static.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: build-static on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v4 - name: Compile debug run: make all env: BUILD_TYPE: Debug BUILD_SHARED_LIBS: OFF BUILD_TESTING: ON BUILD_INTEGRATION: ON BUILD_TOOLS: ON ZENOH_LOG: debug crossbuilds: name: Build on ubuntu-latest runs-on: ubuntu-latest steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: tool-cache: false android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: true - uses: actions/checkout@v4 - name: Crosscompile debug run: make crossbuilds env: BUILD_TYPE: Debug BUILD_SHARED_LIBS: OFF ZENOH_LOG: debug ================================================ FILE: .github/workflows/ci.yml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: CI on: push: branches: ["**"] pull_request: branches: ["**"] schedule: - cron: "0 6 * * 1-5" jobs: run_tests: name: Run unit tests on ubuntu-latest runs-on: ubuntu-latest steps: - name: Setup mbedtls uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Checkout code uses: actions/checkout@v4 - name: Build & run tests run: | sudo apt install -y ninja-build Z_FEATURE_LINK_TLS=1 Z_FEATURE_LOCAL_QUERYABLE=1 Z_FEATURE_LOCAL_SUBSCRIBER=1 Z_FEATURE_UNSTABLE_API=1 CMAKE_GENERATOR=Ninja ASAN=ON make BUILD_TYPE=Debug test - name: Check in-tree generated files are in sync with version.txt run: | if ! git diff --exit-code -- include/zenoh-pico.h library.json; then echo "::error::include/zenoh-pico.h or library.json is out of sync with version.txt." echo "Run a local CMake configure (e.g. 'cmake -S . -B build') and commit the refreshed files." exit 1 fi run_windows_test: name: Run peer unicast test on windows-latest runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build & run tests run: | make all .\build\tests\Debug\z_test_peer_unicast.exe timeout-minutes: 15 shell: cmd env: ZENOH_LOG: INFO Z_FEATURE_UNSTABLE_API: 1 memory_leak_unit_tests: name: Memory leak unit tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup mbedtls uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Build tests run: | sudo apt install -y ninja-build Z_FEATURE_LINK_TLS=1 Z_FEATURE_LOCAL_QUERYABLE=1 Z_FEATURE_LOCAL_SUBSCRIBER=1 Z_FEATURE_UNSTABLE_API=1 CMAKE_GENERATOR=Ninja make BUILD_TYPE=Debug - name: Install valgrind run: | sudo apt update sudo apt install -y valgrind - name: Memory leak unit tests run: | cd build/tests for test in *_test; do if [[ -x "$test" && ! -d "$test" ]]; then # Run only executables echo "Running Valgrind on $test" valgrind --leak-check=full --error-exitcode=1 --suppressions=valgrind.supp "./$test" > /dev/null || exit 1 fi done check_format: name: Check codebase format with clang-format runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v4 - name: Run clang-format dry-run run: | clang-format --version find include/ src/ tests/ examples/ -iname "*.ino" -o -iname "*.h" -o -iname "*.c" | xargs clang-format -n -Werror c99_build: name: Check c99 compilation runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with C99 run: | sudo apt install -y ninja-build FORCE_C99=ON CMAKE_GENERATOR=Ninja make raweth_build: name: Build raweth transport on ubuntu-latest runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build raweth run: | sudo apt install -y ninja-build Z_FEATURE_RAWETH_TRANSPORT=1 CMAKE_GENERATOR=Ninja make tls_build: name: Build TLS transport on ubuntu-latest runs-on: ubuntu-latest steps: - name: Setup mbedtls uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Checkout code uses: actions/checkout@v4 - name: Build with TLS run: | sudo apt update sudo apt install -y ninja-build Z_FEATURE_LINK_TLS=1 CMAKE_GENERATOR=Ninja make zenoh_build: name: Build Zenoh from source runs-on: ubuntu-latest outputs: artifact-name: ${{ steps.main.outputs.artifact-name }} steps: - id: main name: Build Zenoh uses: eclipse-zenoh/ci/build-crates-standalone@main with: repo: eclipse-zenoh/zenoh branch: main artifact-patterns: | ^zenohd$ ^libzenoh_plugin_rest.so$ ^libzenoh_plugin_storage_manager.so$ modular_build: needs: zenoh_build name: Modular build on ubuntu-latest runs-on: ubuntu-latest strategy: matrix: feature_publication: [1, 0] feature_subscription: [1, 0] feature_queryable: [1, 0] feature_query: [1, 0] steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: run-zenoh name: Run Zenoh router run: | RUST_LOG=debug ./zenoh-standalone/zenohd & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Build project run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja make python3 ./build/tests/modularity.py --pub $Z_FEATURE_PUBLICATION --sub $Z_FEATURE_SUBSCRIPTION --queryable $Z_FEATURE_QUERYABLE --query $Z_FEATURE_QUERY timeout-minutes: 15 env: Z_FEATURE_PUBLICATION: ${{ matrix.feature_publication }} Z_FEATURE_SUBSCRIPTION: ${{ matrix.feature_subscription }} Z_FEATURE_QUERYABLE: ${{ matrix.feature_queryable }} Z_FEATURE_QUERY: ${{ matrix.feature_query }} - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} unstable_build: name: Check compilation with unstable API runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with unstable run: | sudo apt install -y ninja-build Z_FEATURE_UNSTABLE_API=1 CMAKE_GENERATOR=Ninja make no_scouting_build: name: Check compilation without scouting runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build without scouting run: | sudo apt install -y ninja-build Z_FEATURE_SCOUTING=0 CMAKE_GENERATOR=Ninja make no_interest_build: name: Check compilation without interests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build without interests run: | sudo apt install -y ninja-build Z_FEATURE_INTEREST=0 CMAKE_GENERATOR=Ninja make no_liveliness_build: name: Check compilation without liveliness runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build without interests run: | sudo apt install -y ninja-build Z_FEATURE_LIVELINESS=0 CMAKE_GENERATOR=Ninja make advanced_publisher_build: name: Check compilation with advanced publisher runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with advanced publisher run: | sudo apt install -y ninja-build Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_ADVANCED_PUBLICATION=1 CMAKE_GENERATOR=Ninja make advanced_subscriber_build: name: Check compilation with advanced subscriber runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with advanced subscriber run: | sudo apt install -y ninja-build Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_ADVANCED_SUBSCRIPTION=1 CMAKE_GENERATOR=Ninja make rx_cache_build: name: Check compilation with RX cache enabled runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with RX cache run: | sudo apt install -y ninja-build Z_FEATURE_RX_CACHE=1 CMAKE_GENERATOR=Ninja make gcc10_build: name: Check compilation with GCC 10 runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with GCC 10 run: | sudo apt update sudo apt install -y ninja-build gcc-10 CC=gcc-10 Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_ADVANCED_PUBLICATION=1 Z_FEATURE_ADVANCED_SUBSCRIPTION=1 CMAKE_GENERATOR=Ninja make gcc9_build: name: Check compilation with Ubuntu 20.04 (GCC 9) runs-on: ubuntu-latest container: ubuntu:20.04 env: DEBIAN_FRONTEND: noninteractive steps: - name: Checkout code uses: actions/checkout@v4 - name: Build with GCC 9 run: | apt update && apt install -y build-essential cmake mkdir build && cd build CC=gcc-9 Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_ADVANCED_PUBLICATION=1 Z_FEATURE_ADVANCED_SUBSCRIPTION=1 cmake .. cmake --build . st_build: needs: zenoh_build name: Build and test in single thread on ubuntu-latest (unstable=${{ matrix.unstable_api }}) runs-on: ubuntu-latest strategy: matrix: unstable_api: [0, 1] steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: run-zenoh name: Run Zenoh router run: | RUST_LOG=debug ./zenoh-standalone/zenohd & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Build project and run tests run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja ASAN=ON make BUILD_TYPE=Debug ninja -C build/ test python3 ./build/tests/single_thread.py timeout-minutes: 15 env: Z_FEATURE_MULTI_THREAD: 0 Z_FEATURE_UNSTABLE_API: ${{ matrix.unstable_api }} - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} fragment_test: needs: zenoh_build name: Test multicast and unicast fragmentation runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: run-zenoh name: Run Zenoh router run: | RUST_LOG=debug ./zenoh-standalone/zenohd & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Build project and run test run: | sudo apt install -y ninja-build cmake -DBATCH_UNICAST_SIZE=4096 -B build/ -G Ninja CMAKE_GENERATOR=Ninja make python3 ./build/tests/fragment.py timeout-minutes: 15 - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} attachment_test: needs: zenoh_build name: Test attachments runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: run-zenoh name: Run Zenoh router run: | RUST_LOG=debug ./zenoh-standalone/zenohd & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Build project and run test run: | sudo apt install -y ninja-build Z_FEATURE_UNSTABLE_API=1 CMAKE_GENERATOR=Ninja make python3 ./build/tests/attachment.py timeout-minutes: 15 - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} memory_leak_test: needs: zenoh_build name: Test examples memory leak runs-on: ubuntu-latest steps: - name: Setup mbedtls uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: run-zenoh name: Run Zenoh router run: | RUST_LOG=debug ./zenoh-standalone/zenohd & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Install valgrind run: | sudo apt-get update sudo apt-get install -y valgrind - name: Build project run: | sudo apt install -y ninja-build Z_FEATURE_LINK_TLS=1 Z_FEATURE_LOCAL_QUERYABLE=1 Z_FEATURE_LOCAL_SUBSCRIBER=1 Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_LIVELINESS=1 CMAKE_GENERATOR=Ninja make - name: Run test run: python3 -u ./build/tests/memory_leak.py timeout-minutes: 15 - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} no_router: name: Test examples without router runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build & test pico run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja ASAN=ON make python3 ./build/tests/no_router.py timeout-minutes: 15 connection_restore_test: needs: zenoh_build name: Connection restore test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - name: Build project and run test run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja ASAN=ON CMAKE_BUILD_TYPE=Debug ZENOH_DEBUG=3 make RUST_LOG=debug sudo python3 ./build/tests/connection_restore.py ./zenoh-standalone/zenohd timeout-minutes: 20 routed_peer_client_test: needs: zenoh_build name: Test routed peer client communication runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Download Zenoh artifacts uses: actions/download-artifact@v4 with: name: ${{ needs.zenoh_build.outputs.artifact-name }} - name: Unzip Zenoh artifacts run: unzip ${{ needs.zenoh_build.outputs.artifact-name }} -d zenoh-standalone - id: select-multicast-iface name: Select multicast-capable interface run: | echo "Detecting multicast interface..." # Pick first non-loopback, non-docker, non-veth interface IFACE=$(ip -o link show | awk -F': ' '$2 != "lo" && $2 !~ /docker/ && $2 !~ /veth/ {print $2; exit}') if [ -z "$IFACE" ]; then echo "No suitable interface found, defaulting to eth0" IFACE=eth0 fi echo "Selected interface: $IFACE" echo "iface=$IFACE" >> "$GITHUB_OUTPUT" - id: run-zenoh name: Run Zenoh router run: | IFACE=${{ steps.select-multicast-iface.outputs.iface }} RUST_LOG=debug ./zenoh-standalone/zenohd -l tcp/127.0.0.1:7447 -l udp/224.0.0.123:7447#iface=$IFACE > zenohd.log 2>&1 & echo "zenohd-pid=$!" >> $GITHUB_OUTPUT - name: Build project and run test run: | sudo apt install -y ninja-build Z_FEATURE_UNSTABLE_API=1 BATCH_MULTICAST_SIZE=8192 BATCH_UNICAST_SIZE=49152 CMAKE_GENERATOR=Ninja make IFACE=${{ steps.select-multicast-iface.outputs.iface }} python3 ./build/tests/routed_peer_client.py tcp/127.0.0.1:7447 udp/224.0.0.123:7447#iface=$IFACE timeout-minutes: 15 - name: Kill Zenoh router if: always() run: kill ${{ steps.run-zenoh.outputs.zenohd-pid }} - name: Print zenohd logs if: always() run: | echo "===== ZENOH ROUTER LOGS =====" cat zenohd.log || echo "zenohd.log not found" unicast_peer_test: name: P2p unicast test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Build & test pico run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja make ./build/tests/z_test_peer_unicast env: Z_FEATURE_UNSTABLE_API: 1 Z_FEATURE_UNICAST_PEER: 1 timeout-minutes: 15 multicast_peer_test: name: P2p multicast test runs-on: ubuntu-latest strategy: matrix: feature_declarations: [1, 0] steps: - name: Checkout code uses: actions/checkout@v4 - name: Build & test pico run: | sudo apt install -y ninja-build CMAKE_GENERATOR=Ninja make ./build/tests/z_test_peer_multicast env: Z_FEATURE_UNSTABLE_API: 1 Z_FEATURE_MULTICAST_DECLARATIONS: ${{ matrix.feature_declarations }} timeout-minutes: 15 markdown_lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: DavidAnson/markdownlint-cli2-action@v18 with: config: ".markdownlint.yaml" globs: "**/README.md" build_shared: name: Build shared libs uses: ./.github/workflows/build-shared.yaml build_static: name: Build static libs uses: ./.github/workflows/build-static.yaml integration: name: Run integration tests uses: ./.github/workflows/integration.yaml arduino_esp32: name: Build Arduino ESP32 uses: ./.github/workflows/arduino_esp32.yaml emscripten: name: Build Emscripten uses: ./.github/workflows/emscripten.yaml espidf: name: Build ESP-IDF uses: ./.github/workflows/espidf.yaml freertos: name: Build FreeRTOS uses: ./.github/workflows/freertos_plus_tcp.yaml mbed: name: Build Mbed uses: ./.github/workflows/mbed.yaml rpi_pico: name: Build Raspberry Pi Pico uses: ./.github/workflows/rpi_pico.yaml zephyr: name: Build Zephyr uses: ./.github/workflows/zephyr.yaml codacy-security-scan: name: Codacy Security Scan uses: ./.github/workflows/codacy-analysis.yml cpp_check: name: CppCheck uses: ./.github/workflows/cpp-check.yaml # NOTE: In GitHub repository settings, the "Require status checks to pass # before merging" branch protection rule ensures that commits are only merged # from branches where specific status checks have passed. These checks are # specified manually as a list of workflow job names. Thus we use this extra # job to signal whether all CI checks have passed. ci: name: CI status checks runs-on: ubuntu-latest needs: [ run_tests, check_format, c99_build, raweth_build, tls_build, zenoh_build, modular_build, unstable_build, no_interest_build, no_liveliness_build, st_build, fragment_test, attachment_test, memory_leak_test, no_router, connection_restore_test, markdown_lint, build_shared, build_static, integration, memory_leak_unit_tests, unicast_peer_test, multicast_peer_test, arduino_esp32, emscripten, espidf, freertos, mbed, rpi_pico, zephyr, codacy-security-scan, cpp_check, ] if: always() steps: - name: Check whether all jobs pass run: echo '${{ toJson(needs) }}' | jq -e 'all(.result == "success")' ================================================ FILE: .github/workflows/codacy-analysis.yml ================================================ # This workflow checks out code, performs a Codacy security scan # and integrates the results with the # GitHub Advanced Security code scanning feature. For more information on # the Codacy security scan action usage and parameters, see # https://github.com/codacy/codacy-analysis-cli-action. # For more information on Codacy Analysis CLI in general, see # https://github.com/codacy/codacy-analysis-cli. name: Codacy Security Scan on: workflow_dispatch: workflow_call: jobs: codacy-security-scan: name: Codacy Security Scan with ${{ matrix.tool }} runs-on: ubuntu-latest strategy: matrix: # List of Codacy-supported tools: https://docs.codacy.com/repositories-configure/codacy-configuration-file/#which-tools-can-be-configured-and-which-name-should-i-use%20%20tool-timeout: tool: [ cppcheck, # static analysis of C/C++ code flawfinder, # a static analysis tool for finding vulnerabilities in C/C++ source code markdownlint, # A Node.js style checker and lint tool for Markdown/CommonMark files shellcheck, # a static analysis tool for shell scripts pylintpython3, # a static code analyser for Python 3 ] steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code uses: actions/checkout@v4 # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI for ${{ matrix.tool }} uses: codacy/codacy-analysis-cli-action@v4.4.7 with: verbose: true output: results-${{ matrix.tool }}.sarif format: sarif # Adjust severity of non-security issues gh-code-scanning-compat: true # Force 0 exit code to allow SARIF file generation # This will handover control about PR rejection to the GitHub side max-allowed-issues: 2147483647 tool: ${{ matrix.tool }} - name: Split SARIF into per-run files run: | mkdir -p sarif-splits total_runs=$(jq '.runs | length' results-${{ matrix.tool }}.sarif) echo "Found $total_runs runs" schema=$(jq -r '.["$schema"]' results-${{ matrix.tool }}.sarif) version=$(jq -r '.version' results-${{ matrix.tool }}.sarif) if [[ -z "$version" || "$version" == "null" ]]; then echo "Error: Missing SARIF version" exit 1 fi for ((i=0; i tmp-run.json echo "{ \"\$schema\": \"$schema\", \"version\": \"$version\", \"runs\": [$(cat tmp-run.json)] }" \ > sarif-splits/${{ matrix.tool }}-${i}.sarif done rm -f tmp-run.json - name: Validate SARIF files run: | for f in sarif-splits/*.sarif; do echo "Validating $f..." jq empty "$f" || { echo "::error ::Invalid JSON in $f"; exit 1; } done # Pre-upload: detect how many files exist - name: Set SARIF file count id: sarif-count run: | count=$(ls sarif-splits/${{ matrix.tool }}-*.sarif | wc -l) echo "count=$count" >> "$GITHUB_OUTPUT" - name: Fail if SARIF run count exceeds 3 if: steps.sarif-count.outputs.count > '3' run: | echo "::error ::Too many SARIF runs detected (${COUNT}). Maximum allowed is 3." exit 1 # Upload SARIF files (max 3 supported here) - name: Upload SARIF run 0 if: steps.sarif-count.outputs.count != '0' uses: github/codeql-action/upload-sarif@v3 with: sarif_file: sarif-splits/${{ matrix.tool }}-0.sarif category: ${{ matrix.tool }}-0 - name: Upload SARIF run 1 if: steps.sarif-count.outputs.count > '1' uses: github/codeql-action/upload-sarif@v3 with: sarif_file: sarif-splits/${{ matrix.tool }}-1.sarif category: ${{ matrix.tool }}-1 - name: Upload SARIF run 2 if: steps.sarif-count.outputs.count > '2' uses: github/codeql-action/upload-sarif@v3 with: sarif_file: sarif-splits/${{ matrix.tool }}-2.sarif category: ${{ matrix.tool }}-2 ================================================ FILE: .github/workflows/cpp-check.yaml ================================================ # # Copyright (c) 2024 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale zenoh Team, # name: cpp-check on: workflow_dispatch: inputs: zenoh_cpp_branch: description: 'Branch of zenoh-cpp to use' required: false default: 'main' workflow_call: inputs: zenoh_cpp_branch: description: 'Branch of zenoh-cpp to use' required: false default: 'main' type: string jobs: build-and-test: name: Build and test zenoh-cpp on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] unstable: [0, 1] steps: - name: checkout zenoh-pico uses: actions/checkout@v3 - name: Install cmake uses: jwlawson/actions-setup-cmake@v2 with: cmake-version: '3.31.x' - name: build zenoh-pico run: | mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DZENOH_LOG=DEBUG -DCMAKE_INSTALL_PREFIX=~/local -DZ_FEATURE_UNSTABLE_API=${{ matrix.unstable }} -DZ_FEATURE_LIVELINESS=1 -DASAN=ON cmake --build . --target install --config Release - name: clone zenoh-cpp run: | git clone https://github.com/eclipse-zenoh/zenoh-cpp.git cd zenoh-cpp git fetch --all git checkout ${{ github.event.inputs.zenoh_cpp_branch || 'main' }} git submodule update --init --recursive - name: build zenoh-cpp run: | cd zenoh-cpp mkdir build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=~/local -DCMAKE_BUILD_TYPE=Release -DZENOHCXX_ZENOHPICO=ON -DZENOHCXX_ZENOHC=OFF cmake --build . --config Release - name: build examples run: | cd zenoh-cpp/build cmake --build . --target examples --config Release - name: build tests run: | cd zenoh-cpp/build cmake --build . --target tests --config Release - name: run tests shell: bash run: | cd zenoh-cpp/build # On macOS, sudo is required to run tests due to LAN access permissions if [[ "${{ matrix.os }}" == "macos-latest" ]]; then sudo ctest -C Release --output-on-failure else ctest -C Release --output-on-failure fi ================================================ FILE: .github/workflows/emscripten.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: emscripten on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: jwlawson/actions-setup-cmake@v1.14 - uses: mymindstorm/setup-emsdk@v12 - name: Compile debug run: | mkdir build emcmake cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_STANDARD=11 -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=OFF -DBUILD_INTEGRATION=OFF -DBUILD_TOOLS=OFF -DZENOH_LOG=debug -DZ_FEATURE_LINK_WS=1 -DZ_FEATURE_LINK_TCP=0 -DZ_FEATURE_LINK_UDP_MULTICAST=0 -DZ_FEATURE_LINK_UDP_UNICAST=0 -DZ_FEATURE_SCOUTING=0 -H. -Bbuild make -C build ================================================ FILE: .github/workflows/espidf.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: espidf on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] env: IDF_COMPONENT_MANAGER: "0" # ← disable pacman to avoid the pydantic crash steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.platformio/.cache key: ${{ runner.os }}-pio - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install PlatformIO Core run: pip install --upgrade platformio - name: Set up project run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ mkdir -p $ESPIDF_BASE cd $ESPIDF_BASE # Temporary workaround: espressif32@7.0.0 pulls in ESP-IDF 6.0 and exposes # the existing PlatformIO source-selection issue after the platform # restructuring from #1188. Remove this pin once PlatformIO integration is # aligned with the new platform structure. pio init -b az-delivery-devkit-v4 \ --project-option="platform=espressif32@6.13.0" \ --project-option="framework=espidf" \ --project-option="build_flags=-DZENOH_ESPIDF -DZENOH_LOG_DEBUG" \ -O "board_build.cmake_extra_args=-DZ_FEATURE_LINK_SERIAL=1" cd $ESPIDF_BASE/lib ln -s $ZENOH_PICO_BASE cd $ESPIDF_BASE - name: Build z_pub example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_pub.c cd $ESPIDF_BASE pio run - name: Build z_sub example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_sub.c cd $ESPIDF_BASE pio run - name: Build z_pull example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_pull.c cd $ESPIDF_BASE pio run - name: Build z_get example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_get.c cd $ESPIDF_BASE pio run - name: Build z_queryable example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_queryable.c cd $ESPIDF_BASE pio run - name: Build z_scout example run: | cd $HOME export ESPIDF_BASE=$HOME/work/espidfproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ESPIDF_BASE/src/* cd $ESPIDF_BASE/src ln -s $ZENOH_PICO_BASE/examples/espidf/z_scout.c cd $ESPIDF_BASE pio run ================================================ FILE: .github/workflows/freertos_plus_tcp.yaml ================================================ # # Copyright (c) 2023 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # Błażej Sowa, # name: freertos_plus_tcp on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: jwlawson/actions-setup-cmake@v1.13 - name: Install requirements run: | sudo apt update sudo apt install -y ninja-build libslirp-dev libglib2.0-dev - name: Build examples run: | cd examples/freertos_plus_tcp cmake -Bbuild -G"Ninja Multi-Config" -DZ_FEATURE_LINK_UDP_MULTICAST=0 -DZ_CONFIG_SOCKET_TIMEOUT=1000 cmake --build ./build --config Debug cmake --build ./build --config Release ================================================ FILE: .github/workflows/integration.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: integration on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest] steps: - name: Clone this repository uses: actions/checkout@v4 - name: Setup mbedtls if: runner.os == 'Linux' uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Install TLS dependencies (macOS) if: runner.os == 'macOS' shell: bash run: | # Install mbedtls@3 if not installed if ! brew list mbedtls@3 >/dev/null 2>&1; then brew install mbedtls@3 fi MBEDTLS_PREFIX="$(brew --prefix mbedtls@3)" # Make this prefix available for later steps echo "MBEDTLS_INSTALL=${MBEDTLS_PREFIX}" >> "$GITHUB_ENV" # Let pkg-config find the v3 .pc files first echo "PKG_CONFIG_PATH=${MBEDTLS_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}" >> "$GITHUB_ENV" # Help CMake find it as a generic prefix echo "CMAKE_PREFIX_PATH=${MBEDTLS_PREFIX}:${CMAKE_PREFIX_PATH}" >> "$GITHUB_ENV" # Needed to *run* binaries/tests that link against libmbedtls.dylib echo "LD_LIBRARY_PATH=${MBEDTLS_PREFIX}/lib:${LD_LIBRARY_PATH}" >> "$GITHUB_ENV" echo "DYLD_LIBRARY_PATH=${MBEDTLS_PREFIX}/lib:${DYLD_LIBRARY_PATH}" >> "$GITHUB_ENV" - name: Compile debug run: make all env: BUILD_TYPE: Debug BUILD_TESTING: OFF BUILD_INTEGRATION: ON Z_FEATURE_LINK_TLS: 1 Z_FEATURE_UNSTABLE_API: 1 Z_FEATURE_LOCAL_QUERYABLE: 1 Z_FEATURE_LOCAL_SUBSCRIBER: 1 Z_FEATURE_ADVANCED_PUBLICATION: 1 Z_FEATURE_ADVANCED_SUBSCRIPTION: 1 Z_FEATURE_ADMIN_SPACE: 1 - name: Test debug run: make test timeout-minutes: 40 env: BUILD_TYPE: Debug # Workaround for Windows as it seems the previous step is being ignored BUILD_TESTING: OFF # Workaround for Windows as it seems the previous step is being ignored BUILD_INTEGRATION: ON # Workaround for Windows as it seems the previous step is being ignored Z_FEATURE_LINK_TLS: 1 Z_FEATURE_LOCAL_QUERYABLE: 1 Z_FEATURE_LOCAL_SUBSCRIBER: 1 Z_FEATURE_UNSTABLE_API: 1 ZENOH_BRANCH: main asan-build: name: Build on ubuntu-latest with ASAN runs-on: ubuntu-latest strategy: fail-fast: false steps: - name: Clone this repository uses: actions/checkout@v4 - name: Setup mbedtls (Linux) if: runner.os == 'Linux' uses: eclipse-zenoh/ci/setup-mbedtls@main with: mbedtls-version: mbedtls-3.6.5 - name: Install TLS dependencies (macOS) if: runner.os == 'macOS' run: brew list mbedtls >/dev/null 2>&1 || brew install mbedtls - name: Compile debug run: make all env: BUILD_TYPE: Debug BUILD_TESTING: OFF BUILD_INTEGRATION: ON Z_FEATURE_LINK_TLS: 1 Z_FEATURE_UNSTABLE_API: 1 Z_FEATURE_LOCAL_QUERYABLE: 1 Z_FEATURE_LOCAL_SUBSCRIBER: 1 Z_FEATURE_ADVANCED_PUBLICATION: 1 Z_FEATURE_ADVANCED_SUBSCRIPTION: 1 ASAN: 1 - name: Test debug run: make test timeout-minutes: 40 env: BUILD_TYPE: Debug # Workaround for Windows as it seems the previous step is being ignored BUILD_TESTING: OFF # Workaround for Windows as it seems the previous step is being ignored BUILD_INTEGRATION: ON # Workaround for Windows as it seems the previous step is being ignored Z_FEATURE_LINK_TLS: 1 Z_FEATURE_UNSTABLE_API: 1 Z_FEATURE_LOCAL_SUBSCRIBER: 1 ZENOH_BRANCH: main ================================================ FILE: .github/workflows/mbed.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: mbed on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-22.04] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.platformio/.cache key: ${{ runner.os }}-pio - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install PlatformIO Core run: pip install --upgrade platformio - name: Set up project run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ mkdir -p $MBED_BASE cd $MBED_BASE pio init -b nucleo_f767zi --project-option="framework=mbed" --project-option="board_build.cmake_extra_args=-DZ_FEATURE_LINK_SERIAL=1" -O "build_flags=-DZENOH_COMPILER_GCC -DZENOH_LOG_DEBUG" cd $MBED_BASE/lib ln -s $ZENOH_PICO_BASE cd $MBED_BASE - name: Build z_pub example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_pub.cpp cd $MBED_BASE pio run - name: Build z_sub example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_sub.cpp cd $MBED_BASE pio run - name: Build z_pull example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_pull.cpp cd $MBED_BASE pio run - name: Build z_get example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_get.cpp cd $MBED_BASE pio run - name: Build z_queryable example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_queryable.cpp cd $MBED_BASE pio run - name: Build z_scout example run: | cd $HOME export MBED_BASE=$HOME/work/mbedproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $MBED_BASE/src/* cd $MBED_BASE/src ln -s $ZENOH_PICO_BASE/examples/mbed/z_scout.cpp cd $MBED_BASE pio run ================================================ FILE: .github/workflows/pr-label-checklists-generate.yml ================================================ name: PR Label Checklists Generate # This workflow runs in the context of the base repository, so be mindful of the permissions granted here and the actions used in the workflow: # For more information see https://docs.github.com/en/enterprise-cloud@latest/actions/reference/security/secure-use#mitigating-the-risks-of-untrusted-code-checkout on: pull_request_target: types: [opened, reopened, labeled, unlabeled, synchronize] # Prevent duplicate runs when swapping labels (unlabeled + labeled events fire together) concurrency: group: pr-checklists-generate-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: checklist: name: Generate permissions: pull-requests: write statuses: write contents: read uses: eclipse-zenoh/ci/.github/workflows/pr-label-checklists-generate.yml@main with: checklists-repo: eclipse-zenoh/ci secrets: github-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/pr-label-checklists-verify.yml ================================================ name: PR Label Checklists Verify # This workflow runs in the context of the base repository, so be mindful of the permissions granted here and the actions used in the workflow: # For more information see https://docs.github.com/en/enterprise-cloud@latest/actions/reference/security/secure-use#mitigating-the-risks-of-untrusted-code-checkout on: pull_request_target: types: [edited] jobs: verify: name: Verify permissions: pull-requests: read statuses: write contents: read uses: eclipse-zenoh/ci/.github/workflows/pr-label-checklists-verify.yml@main secrets: github-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/release.yml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: Release on: schedule: - cron: "0 0 * * 1-5" workflow_dispatch: inputs: live-run: type: boolean description: Live-run required: false version: type: string description: Release number required: false branch: type: string description: Release branch required: false jobs: tag: name: Branch, Bump & tag runs-on: ubuntu-latest outputs: version: ${{ steps.create-release-branch.outputs.version }} branch: ${{ steps.create-release-branch.outputs.branch }} steps: - name: Checkout this repository uses: actions/checkout@v4 with: ref: ${{ inputs.branch || 'main' }} fetch-tags: true fetch-depth: 0 path: version-checkout - id: version run: | if [[ -n "${{ inputs.version }}" ]]; then echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT" else cd version-checkout tag=$(git describe --tags --abbrev=0) tweak=$(git describe --tags --abbrev=1 | cut -d'-' -f2) echo "version=$tag.$tweak" >> "$GITHUB_OUTPUT" fi - id: create-release-branch uses: eclipse-zenoh/ci/create-release-branch@main with: repo: ${{ github.repository }} live-run: ${{ inputs.live-run || false }} version: ${{ steps.version.outputs.version }} branch: ${{ inputs.branch }} github-token: ${{ secrets.BOT_TOKEN_WORKFLOW }} - name: Checkout this repository uses: actions/checkout@v4 with: ref: ${{ steps.create-release-branch.outputs.branch }} fetch-tags: true - name: Bump and tag project run: bash ci/scripts/bump-and-tag.bash env: LIVE_RUN: ${{ inputs.live-run || false }} VERSION: ${{ steps.create-release-branch.outputs.version }} GIT_USER_NAME: eclipse-zenoh-bot GIT_USER_EMAIL: eclipse-zenoh-bot@users.noreply.github.com pre-build: name: Pre-Build needs: tag runs-on: ubuntu-latest outputs: build-linux-matrix: ${{ steps.pre-build.outputs.build-linux-matrix }} steps: - name: Clone this repository uses: actions/checkout@v4 with: ref: ${{ needs.tag.outputs.branch }} - id: pre-build run: bash ci/scripts/pre-build.bash build-macos: name: Build for macOS (x64) needs: [tag, pre-build] runs-on: macos-latest steps: - name: Clone this repository uses: actions/checkout@v4 with: ref: ${{ needs.tag.outputs.branch }} - id: build-macos run: bash ci/scripts/build-macos.bash env: REPO: ${{ github.repository }} VERSION: ${{ needs.tag.outputs.version }} - name: Upload macOS library archive uses: actions/upload-artifact@v4 with: name: ${{ steps.build-macos.outputs.archive-lib }} path: ${{ steps.build-macos.outputs.archive-lib }} - name: Upload macOS examples archive uses: actions/upload-artifact@v4 with: name: ${{ steps.build-macos.outputs.archive-examples }} path: ${{ steps.build-macos.outputs.archive-examples }} build-linux: name: Crossbuild for ${{ matrix.target }} needs: [tag, pre-build] runs-on: ubuntu-latest strategy: fail-fast: false matrix: ${{ fromJson(needs.pre-build.outputs.build-linux-matrix) }} steps: - name: Clone this repository uses: actions/checkout@v4 with: ref: ${{ needs.tag.outputs.branch }} - id: build-linux run: bash ci/scripts/build-linux.bash env: REPO: ${{ github.repository }} VERSION: ${{ needs.tag.outputs.version }} TARGET: ${{ matrix.target }} - name: Upload Linux library archive for ${{ matrix.target }} uses: actions/upload-artifact@v4 with: name: ${{ steps.build-linux.outputs.archive-lib }} path: ${{ steps.build-linux.outputs.archive-lib }} - name: Upload Linux examples archive for ${{ matrix.target }} uses: actions/upload-artifact@v4 with: name: ${{ steps.build-linux.outputs.archive-examples }} path: ${{ steps.build-linux.outputs.archive-examples }} - name: Upload Linux RPM archive for ${{ matrix.target }} uses: actions/upload-artifact@v4 with: name: ${{ steps.build-linux.outputs.archive-rpm }} path: ${{ steps.build-linux.outputs.archive-rpm }} - name: Upload Linux DEB archive for ${{ matrix.target }} uses: actions/upload-artifact@v4 with: name: ${{ steps.build-linux.outputs.archive-deb }} path: ${{ steps.build-linux.outputs.archive-deb }} debian: name: Publish Debian packages needs: [tag, build-linux] uses: eclipse-zenoh/ci/.github/workflows/release-crates-debian.yml@main with: no-build: true live-run: ${{ inputs.live-run || false }} version: ${{ needs.tag.outputs.version }} repo: ${{ github.repository }} branch: ${{ needs.tag.outputs.branch }} secrets: inherit eclipse: needs: [tag, build-macos, build-linux] runs-on: ubuntu-latest steps: - uses: eclipse-zenoh/ci/publish-crates-eclipse@main with: live-run: ${{ inputs.live-run || false }} version: ${{ needs.tag.outputs.version }} ssh-host: genie.zenoh@projects-storage.eclipse.org ssh-host-path: /home/data/httpd/download.eclipse.org/zenoh/zenoh-pico ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} ssh-passphrase: ${{ secrets.SSH_PASSPHRASE }} archive-patterns: '.*\.zip' github: needs: [tag, build-macos, build-linux] runs-on: ubuntu-latest steps: - uses: eclipse-zenoh/ci/publish-crates-github@main with: repo: ${{ github.repository }} live-run: ${{ inputs.live-run || false }} version: ${{ needs.tag.outputs.version }} branch: ${{ needs.tag.outputs.branch }} github-token: ${{ secrets.BOT_TOKEN_WORKFLOW }} archive-patterns: '.*\.zip' ================================================ FILE: .github/workflows/rpi_pico.yaml ================================================ # # Copyright (c) 2024 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: rpi_pico on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} for ${{ matrix.pico_board }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] pico_board: ["pico", "pico_w", "pico2", "pico2_w"] steps: - uses: actions/checkout@v4 - uses: jwlawson/actions-setup-cmake@v1.13 - name: Install requirements run: | sudo apt update sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential g++ libstdc++-arm-none-eabi-newlib - name: Install Raspberry Pico SDK run: | export PICO_SDK_PATH=$HOME/work/pico-sdk mkdir -p $PICO_SDK_PATH cd $PICO_SDK_PATH git clone https://github.com/raspberrypi/pico-sdk.git --branch 2.1.1-correct-picotool . git submodule update --init - name: Install FreeRTOS SDK run: | export FREERTOS_KERNEL_PATH=$HOME/work/FreeRTOS-Kernel/ mkdir -p $FREERTOS_KERNEL_PATH cd $FREERTOS_KERNEL_PATH git clone https://github.com/FreeRTOS/FreeRTOS-Kernel.git . # Some version witch aleready supports the Pico SDK but not the latest, because sometimes latest is broken git checkout e169442c29ba8e26faf033cc0886029dd5812979 git submodule update --init - name: Build examples run: | export PICO_SDK_PATH=$HOME/work/pico-sdk export FREERTOS_KERNEL_PATH=$HOME/work/FreeRTOS-Kernel/ cd $HOME/work/zenoh-pico/zenoh-pico/examples/rpi_pico cmake -Bbuild -DWIFI_SSID=wifi_network_ssid -DWIFI_PASSWORD=wifi_network_password -DPICO_BOARD="$PICO_BOARD" cmake --build ./build env: PICO_BOARD: ${{ matrix.pico_board}} CMAKE_POLICY_VERSION_MINIMUM: 3.5 ================================================ FILE: .github/workflows/update-release-project.yml ================================================ name: Update release project on: issues: types: [opened, edited, labeled] pull_request_target: types: [closed] branches: - main jobs: main: uses: eclipse-zenoh/zenoh/.github/workflows/update-release-project.yml@main secrets: inherit ================================================ FILE: .github/workflows/zephyr.yaml ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # name: zephyr on: workflow_call: jobs: build: name: Build on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest] steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.platformio/.cache key: ${{ runner.os }}-pio - uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install PlatformIO Core run: pip install --upgrade platformio - name: Set up project run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ mkdir -p $ZEPHYR_BASE cd $ZEPHYR_BASE pio init -b nucleo_f767zi --project-option="framework=zephyr" --project-option="board_build.cmake_extra_args=-DZ_FEATURE_LINK_SERIAL=1" --project-option="build_flags=-DZENOH_LOG_DEBUG" cd $ZEPHYR_BASE/lib ln -s $ZENOH_PICO_BASE mkdir -p $ZEPHYR_BASE/zephyr cd $ZEPHYR_BASE/zephyr ln -s $ZENOH_PICO_BASE/docs/zephyr/nucleo_f767zi/prj.conf prj.conf ln -s $ZENOH_PICO_BASE/docs/zephyr/nucleo_f767zi/CMakeLists.txt CMakeLists.txt - name: Build z_pub example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_pub.c cd $ZEPHYR_BASE pio run - name: Build z_sub example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_sub.c cd $ZEPHYR_BASE pio run - name: Build z_pull example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_pull.c cd $ZEPHYR_BASE pio run - name: Build z_get example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_get.c cd $ZEPHYR_BASE pio run - name: Build z_queryable example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_queryable.c cd $ZEPHYR_BASE pio run - name: Build z_scout example run: | cd $HOME export ZEPHYR_BASE=$HOME/work/zephyrproject/ export ZENOH_PICO_BASE=$HOME/work/zenoh-pico/zenoh-pico/ rm -rf $ZEPHYR_BASE/src/* cd $ZEPHYR_BASE/src ln -s $ZENOH_PICO_BASE/examples/zephyr/z_scout.c cd $ZEPHYR_BASE pio run ================================================ FILE: .gitignore ================================================ # CMake build build_default crossbuilds CMakeFiles compile_commands.json # vscode .vscode # IntelliJ / CLion .idea cmake-build-debug cmake-build-release # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb *.sarif .gdb_history # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf .cache ================================================ FILE: .markdownlint.yaml ================================================ { "MD013": false, # Line length limitation "MD033": false, # Enable Inline HTML "MD041": false, # Allow first line heading "MD045": false, # Allow Images have no alternate text } ================================================ FILE: .readthedocs.yaml ================================================ # .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" apt_packages: - libclang-dev # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt ================================================ FILE: BSDmakefile ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # .PHONY: test clean # Build type. This set the CMAKE_BUILD_TYPE variable. # Accepted values: Release, Debug, GCov BUILD_TYPE?=Release # Build examples. This sets the BUILD_EXAMPLES variable. # Accepted values: ON, OFF BUILD_EXAMPLES?=ON # Build testing. This sets the BUILD_TESTING variable. # Accepted values: ON, OFF BUILD_TESTING?=ON # Build integration tests. This sets the BUILD_INTEGRATION variable. # Accepted values: ON, OFF BUILD_INTEGRATION?=OFF # Build integration tests. This sets the BUILD_TOOLS variable. # Accepted values: ON, OFF BUILD_TOOLS?=OFF # Logging level. This sets the ZENOH_LOG variable. # Accepted values (empty string means no log): # ERROR/error # WARN/warn # INFO/info # DEBUG/debug # TRACE/trace ZENOH_LOG?="" # zenoh-pico/ directory ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # Build directory BUILD_DIR=build # Crossbuild directory CROSSBUILD_TARGETS=linux-armv5 linux-armv6 linux-armv7 linux-armv7a linux-arm64 linux-mips linux-x86 linux-x64 CROSSBUILD_DIR=crossbuilds CROSSIMG_PREFIX=zenoh-pico_ # NOTES: # - ARM: old versions of dockcross/dockcross were creating some issues since they used an old GCC (4.8.3) which lacks (even using -std=gnu11) CMAKE_OPT=-DZENOH_DEBUG=$(ZENOH_DEBUG) -DZENOH_LOG=$(ZENOH_LOG) -DZENOH_LOG_PRINT=$(ZENOH_LOG_PRINT) -DBUILD_EXAMPLES=$(BUILD_EXAMPLES) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DBUILD_TESTING=$(BUILD_TESTING) -DBUILD_INTEGRATION=$(BUILD_INTEGRATION) -DBUILD_TOOLS=$(BUILD_TOOLS) -DBUILD_SHARED_LIBS=$(BUILD_SHARED_LIBS) -H. all: make $(BUILD_DIR)/Makefile: mkdir -p $(BUILD_DIR) echo $(CMAKE_OPT) cmake $(CMAKE_OPT) -B$(BUILD_DIR) make: $(BUILD_DIR)/Makefile cmake --build $(BUILD_DIR) install: $(BUILD_DIR)/Makefile cmake --install $(BUILD_DIR) test: make ctest --verbose --test-dir build crossbuilds: $(CROSSBUILD_TARGETS) DOCKER_OK := $(shell docker version 2> /dev/null) check-docker: .ifndef DOCKER_OK $(error "Docker is not available. Please install Docker") .endif crossbuild: check-docker @echo "FROM dockcross/$(CROSSIMG)\nRUN apt-get update && apt-get -y install rpm" | docker build -t $(CROSSIMG_PREFIX)$(CROSSIMG) - docker run --rm -v $(ROOT_DIR):/workdir -w /workdir $(CROSSIMG_PREFIX)$(CROSSIMG) bash -c "\ cmake $(CMAKE_OPT) -DPACKAGING=DEB,RPM -DDEBARCH=$(DEBARCH) -DRPMARCH=$(RPMARCH) -B$(CROSSBUILD_DIR)/$(CROSSIMG) && \ make VERBOSE=1 -C$(CROSSBUILD_DIR)/$(CROSSIMG) all package" docker rmi $(CROSSIMG_PREFIX)$(CROSSIMG) linux-armv5: CROSSIMG=$@ DEBARCH=arm RPMARCH=arm make crossbuild linux-armv6: CROSSIMG=$@ DEBARCH=arm RPMARCH=arm make crossbuild linux-armv7: CROSSIMG=$@ DEBARCH=armhf RPMARCH=armhf make crossbuild linux-armv7a: CROSSIMG=$@ DEBARCH=armhf RPMARCH=armhf make crossbuild linux-arm64: CROSSIMG=$@ DEBARCH=arm64 RPMARCH=aarch64 make crossbuild linux-mips: CROSSIMG=$@ DEBARCH=mips RPMARCH=mips make crossbuild linux-x86: CROSSIMG=$@ DEBARCH=i386 RPMARCH=x86 make crossbuild linux-x64: CROSSIMG=$@ DEBARCH=amd64 RPMARCH=x86_64 make crossbuild clean: rm -fr $(BUILD_DIR) rm -rf $(CROSSBUILD_DIR) ================================================ FILE: CMakeLists.txt ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # cmake_minimum_required(VERSION 3.14) file(READ ${CMAKE_CURRENT_SOURCE_DIR}/version.txt version) project(zenohpico VERSION ${version} LANGUAGES C) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) include(helpers) include(platforms) # Configure header file to define the project version set(ZENOH_PICO ${PROJECT_VERSION}) set(ZENOH_PICO_MAJOR ${PROJECT_VERSION_MAJOR}) set(ZENOH_PICO_MINOR ${PROJECT_VERSION_MINOR}) set(ZENOH_PICO_PATCH ${PROJECT_VERSION_PATCH}) if(PROJECT_VERSION_TWEAK STREQUAL "") set(ZENOH_PICO_TWEAK 0) else() set(ZENOH_PICO_TWEAK ${PROJECT_VERSION_TWEAK}) endif() configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/zenoh-pico.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/zenoh-pico.h @ONLY ) include(CMakePackageConfigHelpers) include(GNUInstallDirs) option(BUILD_SHARED_LIBS "Build shared libraries if ON, otherwise build static libraries" ON) set(ZP_PLATFORM "" CACHE STRING "Built-in or external platform profile") set(ZP_EXTERNAL_PACKAGES "" CACHE STRING "Optional external CMake packages to load") set(ZENOH_LOG "" CACHE STRING "Use this to set the ZENOH_LOG variable") set(ZENOH_LOG_PRINT "" CACHE STRING "Use this to set the ZENOH_LOG_PRINT variable") set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") if(CMAKE_EXPORT_COMPILE_COMMANDS) set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) endif() set(ZP_PLATFORM_DIRS "") set(ZP_EXTERNAL_IMPORTED_TARGETS "") set(ZP_EXTERNAL_IMPORTED_TARGET_PACKAGES "") if(NOT ZP_EXTERNAL_PACKAGES STREQUAL "") foreach(_zp_external_package IN LISTS ZP_EXTERNAL_PACKAGES) zp_find_external_package("${_zp_external_package}") endforeach() endif() find_package(zenohpico_platform CONFIG REQUIRED CONFIGS platformConfig.cmake PATHS "${CMAKE_CURRENT_SOURCE_DIR}/cmake" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) if(DEFINED ZP_SYSTEM_LAYER AND NOT "${ZP_SYSTEM_LAYER}" STREQUAL "") message(FATAL_ERROR "ZP_SYSTEM_LAYER is no longer supported. Use ZP_PLATFORM instead.") endif() if(ZP_PLATFORM STREQUAL "") zp_detect_default_platform(_zp_default_platform) set(ZP_PLATFORM "${_zp_default_platform}") endif() if(ZP_PLATFORM STREQUAL "") message(FATAL_ERROR "No platform profile selected. Set ZP_PLATFORM.") endif() if(DEFINED Z_FEATURE_LINK_UDP_MULTICAST) set(_zp_link_udp_multicast "${Z_FEATURE_LINK_UDP_MULTICAST}") else() set(_zp_link_udp_multicast "1") endif() if(DEFINED Z_FEATURE_MULTICAST_TRANSPORT) set(_zp_multicast_transport "${Z_FEATURE_MULTICAST_TRANSPORT}") else() set(_zp_multicast_transport "1") endif() if("${_zp_link_udp_multicast}" STREQUAL "1" AND "${_zp_multicast_transport}" STREQUAL "1") set(ZP_UDP_MULTICAST_ENABLED ON) else() set(ZP_UDP_MULTICAST_ENABLED OFF) endif() zp_load_platform_profile("${ZP_PLATFORM}") if(NOT ZP_PLATFORM_SYSTEM_LAYER STREQUAL "") set(ZP_SYSTEM_LAYER "${ZP_PLATFORM_SYSTEM_LAYER}") else() set(ZP_SYSTEM_LAYER "${ZP_PLATFORM}") endif() set(ZP_SYSTEM_PLATFORM_HEADER "${ZP_PLATFORM_SYSTEM_PLATFORM_HEADER}") if(CMAKE_SYSTEM_NAME MATCHES "Windows") if (BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) endif() elseif(CMAKE_SYSTEM_NAME MATCHES "Generic") if(ZP_SYSTEM_LAYER STREQUAL "zephyr") set(PACKAGING OFF) # no packaging support for zephyr set(BUILD_SHARED_LIBS "OFF") endif() endif() # Language options if(NOT CMAKE_C_STANDARD) if(c_std_11 IN_LIST CMAKE_C_COMPILE_FEATURES) set(CMAKE_C_STANDARD 11) message(STATUS "Setting C11 as the C Standard") else() # C99 pedantic doesn't like unix header anonymous structure set(CMAKE_C_STANDARD 99) message(STATUS "Setting C99 as the C Standard") endif() endif() set(CMAKE_C_STANDARD_REQUIRED TRUE) # Use cmake .. -DCMAKE_BUILD_TYPE=DEBUG for debug if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RELEASE) endif() string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE) # Compile options if(CMAKE_BUILD_TYPE MATCHES "RELEASE" OR "Release") if(UNIX) add_compile_options(-pipe -O3) elseif(CMAKE_SYSTEM_NAME MATCHES "Generic") add_compile_options(-pipe -O3) endif() else() if(CMAKE_SYSTEM_NAME MATCHES "PICO") add_compile_options(-c -Wall -Wextra -Wno-unused -Wno-strict-prototypes -pipe -g -O0) elseif(UNIX) add_compile_options(-c -Wall -Wextra -Werror -Wshadow -Wunused -Wstrict-prototypes -pipe -g -O0) # C99 pedantic doesn't like struct anonymous in unix header if (NOT CMAKE_C_STANDARD STREQUAL "99") add_compile_options(-Wpedantic) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") add_compile_options(-Wconversion) endif() elseif(MSVC) add_compile_options(/W4 /WX /Od /wd4127) elseif(CMAKE_SYSTEM_NAME MATCHES "Generic") add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -pipe -g -O0) endif() endif() if (PACKAGING) set(PICO_STATIC ON) set(PICO_SHARED ON) endif() if(BUILD_SHARED_LIBS) set(PICO_SHARED ON) else() set(PICO_STATIC ON) endif() set(Libname "zenohpico") if(PICO_STATIC) add_library(${Libname}_static STATIC) set_target_properties(${Libname}_static PROPERTIES OUTPUT_NAME ${Libname}) add_library(zenohpico::static ALIAS ${Libname}_static) endif() if(PICO_SHARED) add_library(${Libname}_shared SHARED) set_target_properties(${Libname}_shared PROPERTIES OUTPUT_NAME ${Libname}) add_library(zenohpico::shared ALIAS ${Libname}_shared) endif() if(BUILD_SHARED_LIBS) add_library(zenohpico::lib ALIAS ${Libname}_shared) else() add_library(zenohpico::lib ALIAS ${Libname}_static) endif() function(pico_add_compile_definition value) add_definitions(-D${value}) if(PICO_STATIC) target_compile_definitions(zenohpico_static PUBLIC ${value}) endif() if(PICO_SHARED) target_compile_definitions(zenohpico_shared PUBLIC ${value}) endif() endfunction() function(pico_target_link_library value) if(PICO_STATIC) target_link_libraries(zenohpico_static PUBLIC ${value}) endif() if(PICO_SHARED) target_link_libraries(zenohpico_shared PRIVATE ${value}) endif() endfunction() pico_add_compile_definition(ZENOH_C_STANDARD=${CMAKE_C_STANDARD}) if (NOT CMAKE_BUILD_TYPE MATCHES "RELEASE" OR "Release") # while in development, use timestamp for patch version: string(TIMESTAMP PROJECT_VERSION_PATCH "%Y%m%ddev") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") endif() if(ZP_SYSTEM_LAYER STREQUAL "macos") set(MACOSX_RPATH "ON") endif() set(_zp_platform_globbed_sources "") foreach(_zp_platform_source_glob IN LISTS ZP_PLATFORM_SOURCE_GLOBS) file(GLOB _zp_globbed_platform_sources ${_zp_platform_source_glob}) list(APPEND _zp_platform_globbed_sources ${_zp_globbed_platform_sources}) endforeach() set(_zp_platform_sources ${_zp_platform_globbed_sources} ${ZP_PLATFORM_SOURCE_FILES}) set(_zp_platform_include_dirs ${ZP_PLATFORM_INCLUDE_DIRS}) set(_zp_platform_compile_definitions ${ZP_PLATFORM_COMPILE_DEFINITIONS}) set(_zp_platform_compile_options ${ZP_PLATFORM_COMPILE_OPTIONS}) set(_zp_platform_link_libraries ${ZP_PLATFORM_LINK_LIBRARIES}) set(_zp_platform_imported_targets "") foreach(_zp_platform_target IN LISTS _zp_platform_link_libraries) if(NOT TARGET "${_zp_platform_target}") continue() endif() get_property(_zp_is_imported TARGET "${_zp_platform_target}" PROPERTY IMPORTED) if(NOT _zp_is_imported) message(FATAL_ERROR "Platform from ${ZP_PLATFORM_PROFILE_FILE} requires " "ZP_PLATFORM_LINK_LIBRARIES targets to be imported/exported package targets. " "Local targets created directly inside package config files are not supported " "because they break static install/export paths.") endif() get_target_property(_zp_platform_target_type "${_zp_platform_target}" TYPE) if(_zp_platform_target_type STREQUAL "INTERFACE_LIBRARY") get_target_property_if_set(_zp_interface_sources "${_zp_platform_target}" INTERFACE_SOURCES) if(NOT "${_zp_interface_sources}" STREQUAL "") message(FATAL_ERROR "External platform target ${_zp_platform_target} must not use INTERFACE_SOURCES. " "Define a real library target instead.") endif() endif() list(APPEND _zp_platform_imported_targets "${_zp_platform_target}") endforeach() list(REMOVE_DUPLICATES _zp_platform_imported_targets) zp_source_list_requires_cxx(_zp_platform_uses_cxx ${_zp_platform_sources}) if(_zp_platform_uses_cxx) set(ZP_USES_CXX ON) else() set(ZP_USES_CXX OFF) foreach(_zp_platform_target IN LISTS _zp_platform_imported_targets) zp_target_requires_cxx("${_zp_platform_target}" _zp_platform_target_uses_cxx) if(_zp_platform_target_uses_cxx) set(ZP_USES_CXX ON) break() endif() endforeach() endif() if(ZP_USES_CXX) enable_language(CXX) endif() foreach(_zp_platform_definition IN LISTS _zp_platform_compile_definitions) pico_add_compile_definition(${_zp_platform_definition}) endforeach() # Compiler definition message("Compilers in use: ${CMAKE_C_COMPILER_ID}, ${CMAKE_CXX_COMPILER_ID}") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") pico_add_compile_definition(ZENOH_COMPILER_CLANG) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "GNU") pico_add_compile_definition(ZENOH_COMPILER_GCC) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_C_COMPILER_ID STREQUAL "Intel") pico_add_compile_definition(ZENOH_COMPILER_INTEL) elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_C_COMPILER_ID STREQUAL "MSVC") pico_add_compile_definition(ZENOH_COMPILER_MSVC) else() pico_add_compile_definition(ZENOH_COMPILER_OTHER) endif() # Logging if (DEFINED ZENOH_DEBUG AND NOT (ZENOH_DEBUG STREQUAL "")) pico_add_compile_definition(ZENOH_DEBUG=${ZENOH_DEBUG}) elseif (ZENOH_LOG STREQUAL ERROR OR ZENOH_LOG STREQUAL error) pico_add_compile_definition(ZENOH_LOG_ERROR) elseif (ZENOH_LOG STREQUAL WARN OR ZENOH_LOG STREQUAL warn) pico_add_compile_definition(ZENOH_LOG_WARN) elseif (ZENOH_LOG STREQUAL INFO OR ZENOH_LOG STREQUAL info) pico_add_compile_definition(ZENOH_LOG_INFO) elseif (ZENOH_LOG STREQUAL DEBUG OR ZENOH_LOG STREQUAL debug) pico_add_compile_definition(ZENOH_LOG_DEBUG) elseif (ZENOH_LOG STREQUAL TRACE OR ZENOH_LOG STREQUAL trace) pico_add_compile_definition(ZENOH_LOG_TRACE) endif() if (NOT(ZENOH_LOG_PRINT STREQUAL "")) pico_add_compile_definition(ZENOH_LOG_PRINT=${ZENOH_LOG_PRINT}) endif() add_compile_definitions("Z_BUILD_LOG=$") # Zenoh pico feature configuration options set(FRAG_MAX_SIZE 4096 CACHE STRING "Use this to override the maximum size for fragmented messages") set(BATCH_UNICAST_SIZE 2048 CACHE STRING "Use this to override the maximum unicast batch size") set(BATCH_MULTICAST_SIZE 2048 CACHE STRING "Use this to override the maximum multicast batch size") set(Z_CONFIG_SOCKET_TIMEOUT 100 CACHE STRING "Default socket timeout in milliseconds") set(Z_TRANSPORT_LEASE 10000 CACHE STRING "Link lease duration in milliseconds to announce to other zenoh nodes") set(Z_TRANSPORT_LEASE_EXPIRE_FACTOR 3 CACHE STRING "Default session lease expire factor.") set(Z_RUNTIME_MAX_TASKS 64 CACHE STRING "Maximum number of tasks in zenoh-pico's runtime") set(Z_TRANSPORT_ACCEPT_TIMEOUT 1000 CACHE STRING "Link accept timeout in P2P mode in milliseconds") set(Z_TRANSPORT_CONNECT_TIMEOUT 10000 CACHE STRING "Link connect timeout in P2P mode inmilliseconds") set(Z_FEATURE_UNSTABLE_API 0 CACHE STRING "Toggle unstable Zenoh-C API") set(Z_FEATURE_CONNECTIVITY 0 CACHE STRING "Toggle connectivity status/events API (unstable)") set(Z_FEATURE_PUBLICATION 1 CACHE STRING "Toggle publication feature") set(Z_FEATURE_ADVANCED_PUBLICATION 0 CACHE STRING "Toggle advanced publication feature") set(Z_FEATURE_SUBSCRIPTION 1 CACHE STRING "Toggle subscription feature") set(Z_FEATURE_ADVANCED_SUBSCRIPTION 0 CACHE STRING "Toggle advanced subscription feature") set(Z_FEATURE_QUERY 1 CACHE STRING "Toggle query feature") set(Z_FEATURE_QUERYABLE 1 CACHE STRING "Toggle queryable feature") set(Z_FEATURE_LIVELINESS 1 CACHE STRING "Toggle liveliness feature") set(Z_FEATURE_INTEREST 1 CACHE STRING "Toggle interests") set(Z_FEATURE_FRAGMENTATION 1 CACHE STRING "Toggle fragmentation") set(Z_FEATURE_ENCODING_VALUES 1 CACHE STRING "Toggle encoding values") set(Z_FEATURE_MULTI_THREAD 1 CACHE STRING "Toggle multithread") set(Z_FEATURE_LINK_TCP 1 CACHE STRING "Toggle TCP links") set(Z_FEATURE_LINK_BLUETOOTH 0 CACHE STRING "Toggle Bluetooth links") set(Z_FEATURE_LINK_WS 0 CACHE STRING "Toggle WebSocket links") set(Z_FEATURE_LINK_SERIAL 0 CACHE STRING "Toggle Serial links") set(Z_FEATURE_LINK_SERIAL_USB 0 CACHE STRING "Toggle Serial USB links") set(Z_FEATURE_LINK_TLS 0 CACHE STRING "Toggle TLS links") set(Z_FEATURE_SCOUTING 1 CACHE STRING "Toggle UDP scouting") set(Z_FEATURE_LINK_UDP_MULTICAST 1 CACHE STRING "Toggle UDP multicast links") set(Z_FEATURE_LINK_UDP_UNICAST 1 CACHE STRING "Toggle UDP unicast links") set(Z_FEATURE_MULTICAST_TRANSPORT 1 CACHE STRING "Toggle multicast transport") set(Z_FEATURE_UNICAST_TRANSPORT 1 CACHE STRING "Toggle unicast transport") set(Z_FEATURE_RAWETH_TRANSPORT 0 CACHE STRING "Toggle raw ethernet transport") set(Z_FEATURE_TCP_NODELAY 1 CACHE STRING "Toggle TCP_NODELAY") set(Z_FEATURE_LOCAL_SUBSCRIBER 0 CACHE STRING "Toggle local subscriptions") set(Z_FEATURE_SESSION_CHECK 1 CACHE STRING "Toggle publisher/querier session check") set(Z_FEATURE_BATCHING 1 CACHE STRING "Toggle batching") set(Z_FEATURE_BATCH_TX_MUTEX 0 CACHE STRING "Toggle tx mutex lock at a batch level") set(Z_FEATURE_BATCH_PEER_MUTEX 0 CACHE STRING "Toggle peer mutex lock at a batch level") set(Z_FEATURE_MATCHING 1 CACHE STRING "Toggle matching feature") set(Z_FEATURE_RX_CACHE 0 CACHE STRING "Toggle RX_CACHE") set(Z_FEATURE_UNICAST_PEER 1 CACHE STRING "Toggle Unicast peer mode") set(Z_FEATURE_AUTO_RECONNECT 1 CACHE STRING "Toggle automatic reconnection") set(Z_FEATURE_MULTICAST_DECLARATIONS 0 CACHE STRING "Toggle multicast resource declarations") set(Z_FEATURE_LOCAL_QUERYABLE 0 CACHE STRING "Toggle local queriables") set(Z_FEATURE_ADMIN_SPACE 0 CACHE STRING "Toggle admin space support") # Add a warning message if someone tries to enable Z_FEATURE_LINK_SERIAL_USB directly if(Z_FEATURE_LINK_SERIAL_USB AND NOT Z_FEATURE_UNSTABLE_API) message(WARNING "Z_FEATURE_LINK_SERIAL_USB can only be enabled when Z_FEATURE_UNSTABLE_API is also enabled. Disabling Z_FEATURE_LINK_SERIAL_USB.") set(Z_FEATURE_LINK_SERIAL_USB 0 CACHE STRING "Toggle Serial USB links" FORCE) endif() if(Z_FEATURE_LINK_WS AND NOT ZP_SYSTEM_LAYER STREQUAL "emscripten") message(FATAL_ERROR "Z_FEATURE_LINK_WS is currently only supported on the emscripten platform.") endif() if(Z_FEATURE_CONNECTIVITY AND NOT Z_FEATURE_UNSTABLE_API) message(WARNING "Z_FEATURE_CONNECTIVITY can only be enabled when Z_FEATURE_UNSTABLE_API is also enabled. Disabling Z_FEATURE_CONNECTIVITY.") set(Z_FEATURE_CONNECTIVITY 0 CACHE STRING "Toggle connectivity status/events API (unstable)" FORCE) endif() if(Z_FEATURE_MATCHING AND NOT Z_FEATURE_INTEREST) message(STATUS "Z_FEATURE_MATCHING can only be enabled when Z_FEATURE_INTEREST is also enabled. Disabling Z_FEATURE_MATCHING.") set(Z_FEATURE_MATCHING 0 CACHE STRING "Toggle matching feature" FORCE) endif() if(Z_FEATURE_SCOUTING AND NOT Z_FEATURE_LINK_UDP_UNICAST) message(STATUS "Z_FEATURE_SCOUTING disabled because Z_FEATURE_LINK_UDP_UNICAST disabled") set(Z_FEATURE_SCOUTING 0 CACHE STRING "Toggle scouting feature" FORCE) endif() if(ZP_SYSTEM_LAYER STREQUAL "freertos_plus_tcp" AND Z_FEATURE_LINK_UDP_MULTICAST) message(STATUS "Z_FEATURE_LINK_UDP_MULTICAST disabled for FreeRTOS-Plus-TCP system layer") set(Z_FEATURE_LINK_UDP_MULTICAST 0 CACHE STRING "Toggle UDP multicast links" FORCE) endif() if(Z_FEATURE_ADVANCED_PUBLICATION AND (NOT Z_FEATURE_UNSTABLE_API OR NOT Z_FEATURE_PUBLICATION OR NOT Z_FEATURE_LIVELINESS)) message(WARNING "Z_FEATURE_ADVANCED_PUBLICATION can only be enabled when Z_FEATURE_UNSTABLE_API, Z_FEATURE_PUBLICATION and Z_FEATURE_LIVELINESS is also enabled. Disabling Z_FEATURE_ADVANCED_PUBLICATION.") set(Z_FEATURE_ADVANCED_PUBLICATION 0 CACHE STRING "Toggle advanced publication feature" FORCE) endif() if(Z_FEATURE_ADMIN_SPACE AND NOT Z_FEATURE_UNSTABLE_API) message(WARNING "Z_FEATURE_ADMIN_SPACE can only be enabled when Z_FEATURE_UNSTABLE_API is enabled. Disabling Z_FEATURE_ADMIN_SPACE.") set(Z_FEATURE_ADMIN_SPACE 0 CACHE STRING "Toggle admin space support" FORCE) endif() if(Z_FEATURE_ADMIN_SPACE AND NOT Z_FEATURE_QUERYABLE) message(WARNING "Z_FEATURE_ADMIN_SPACE can only be enabled when Z_FEATURE_QUERYABLE is enabled. Disabling Z_FEATURE_ADMIN_SPACE.") set(Z_FEATURE_ADMIN_SPACE 0 CACHE STRING "Toggle admin space support" FORCE) endif() message(STATUS "Building with feature config:\n\ * UNSTABLE_API: ${Z_FEATURE_UNSTABLE_API}\n\ * CONNECTIVITY: ${Z_FEATURE_CONNECTIVITY}\n\ * MULTI-THREAD: ${Z_FEATURE_MULTI_THREAD}\n\ * PUBLICATION: ${Z_FEATURE_PUBLICATION}\n\ * SUBSCRIPTION: ${Z_FEATURE_SUBSCRIPTION}\n\ * ADVANCED PUBLICATION: ${Z_FEATURE_ADVANCED_PUBLICATION}\n\ * ADVANCED SUBSCRIPTION: ${Z_FEATURE_ADVANCED_SUBSCRIPTION}\n\ * QUERY: ${Z_FEATURE_QUERY}\n\ * QUERYABLE: ${Z_FEATURE_QUERYABLE}\n\ * LIVELINESS: ${Z_FEATURE_LIVELINESS}\n\ * INTEREST: ${Z_FEATURE_INTEREST}\n\ * AUTO_RECONNECT: ${Z_FEATURE_AUTO_RECONNECT}\n\ * MATCHING: ${Z_FEATURE_MATCHING}\n\ * RAWETH: ${Z_FEATURE_RAWETH_TRANSPORT}\n\ * ADMIN SPACE: ${Z_FEATURE_ADMIN_SPACE}") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/zenoh-pico/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/zenoh-pico/config.h @ONLY ) # Print summary of CMAKE configurations message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode") message(STATUS "Build shared library: ${BUILD_SHARED_LIBS}") message(STATUS "Zenoh Level Log: ${ZENOH_LOG}") message(STATUS "Fragmented message max size: ${FRAG_MAX_SIZE}") message(STATUS "Unicast batch max size: ${BATCH_UNICAST_SIZE}") message(STATUS "Multicast batch max size: ${BATCH_MULTICAST_SIZE}") if(NOT ZP_PLATFORM STREQUAL "") message(STATUS "Platform profile: ${ZP_PLATFORM}") else() message(STATUS "Platform profile: none") endif() message(STATUS "System layer: ${ZP_SYSTEM_LAYER}") message(STATUS "Configuring for ${CMAKE_SYSTEM_NAME}") if(SKBUILD) set(INSTALL_RPATH "zenoh") set(INSTALL_NAME_DIR "zenoh") set(INSTALL_INCLUDE_NAME_DIR "zenoh/include") endif() set(THREADS_PREFER_PTHREAD_FLAG ON) if(CHECK_THREADS) find_package(Threads REQUIRED) endif() if(MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /std:c11 /experimental:c11atomics") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest /experimental:c11atomics") endif() if(PICO_STATIC) target_include_directories(${Libname}_static PUBLIC $ $ $) endif() if(PICO_SHARED) target_include_directories(${Libname}_shared PUBLIC $ $ $) endif() # TLS support via Mbed TLS if(Z_FEATURE_LINK_TLS) find_package(PkgConfig REQUIRED) # Prefer Mbed TLS 3.x, fall back to 2.x # Never accept 4.x or newer # pkg_search_module(MBEDTLS REQUIRED mbedtls-3>=3.0.0 mbedtls>=3.0.0 mbedtls>=2.0.0 ) if(NOT MBEDTLS_VERSION VERSION_LESS "4.0.0") message(FATAL_ERROR "Mbed TLS ${MBEDTLS_VERSION} is not supported. " "Please install Mbed TLS 2.x or 3.x.") endif() message(STATUS "Using Mbed TLS ${MBEDTLS_VERSION}") pkg_search_module(MBEDX509 OPTIONAL mbedx509-3>=3.0.0 mbedx509>=3.0.0 mbedx509>=2.0.0) pkg_search_module(MBEDCRYPTO OPTIONAL mbedcrypto-3>=3.0.0 mbedcrypto>=3.0.0 mbedcrypto>=2.0.0) set(MBEDTLS_LINK_LIBRARIES ${MBEDTLS_LIBRARIES} ) if(MBEDX509_FOUND) if(MBEDX509_VERSION AND NOT MBEDX509_VERSION VERSION_EQUAL MBEDTLS_VERSION) message(FATAL_ERROR "mbedtls (${MBEDTLS_VERSION}) and mbedx509 (${MBEDX509_VERSION}) " "versions do not match.") endif() list(APPEND MBEDTLS_LINK_LIBRARIES ${MBEDX509_LIBRARIES}) endif() if(MBEDCRYPTO_FOUND) if(MBEDCRYPTO_VERSION AND NOT MBEDCRYPTO_VERSION VERSION_EQUAL MBEDTLS_VERSION) message(FATAL_ERROR "mbedtls (${MBEDTLS_VERSION}) and mbedcrypto (${MBEDCRYPTO_VERSION}) " "versions do not match.") endif() list(APPEND MBEDTLS_LINK_LIBRARIES ${MBEDCRYPTO_LIBRARIES}) endif() if(PICO_STATIC) target_include_directories(${Libname}_static PUBLIC ${MBEDTLS_INCLUDE_DIRS}) target_link_directories(${Libname}_static PUBLIC ${MBEDTLS_LIBRARY_DIRS}) target_link_libraries(${Libname}_static PUBLIC ${MBEDTLS_LINK_LIBRARIES}) endif() if(PICO_SHARED) target_include_directories(${Libname}_shared PUBLIC ${MBEDTLS_INCLUDE_DIRS}) target_link_directories(${Libname}_shared PUBLIC ${MBEDTLS_LIBRARY_DIRS}) target_link_libraries(${Libname}_shared PUBLIC ${MBEDTLS_LINK_LIBRARIES}) endif() endif() file(GLOB_RECURSE Sources "src/api/*.c" "src/collections/*.c" "src/net/*.c" "src/protocol/*.c" "src/runtime/*.c" "src/session/*.c" "src/transport/*.c" "src/utils/*.c" "src/system/common/*.c" ) file(GLOB LinkSources "src/link/config/*.c" "src/link/multicast/*.c" "src/link/unicast/*.c" "src/link/transport/common/*.c" ) list(APPEND Sources ${LinkSources}) list(APPEND Sources "${PROJECT_SOURCE_DIR}/src/link/link.c" "${PROJECT_SOURCE_DIR}/src/link/endpoint.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/address.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/address.c" "${PROJECT_SOURCE_DIR}/src/link/transport/upper/serial_protocol.c" "${PROJECT_SOURCE_DIR}/src/link/transport/upper/tls_stream.c" ) list(APPEND Sources ${_zp_platform_sources}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) link_directories(${LIBRARY_OUTPUT_PATH}) if(PICO_STATIC) target_sources(zenohpico_static PRIVATE ${Sources}) endif() if(PICO_SHARED) target_sources(zenohpico_shared PRIVATE ${Sources}) endif() list(REMOVE_DUPLICATES _zp_platform_include_dirs) if(NOT "${_zp_platform_include_dirs}" STREQUAL "") set(_zp_build_interface_platform_include_dirs "") foreach(_zp_platform_include_dir IN LISTS _zp_platform_include_dirs) list(APPEND _zp_build_interface_platform_include_dirs "$") endforeach() if(PICO_STATIC) target_include_directories(zenohpico_static PUBLIC ${_zp_build_interface_platform_include_dirs}) endif() if(PICO_SHARED) target_include_directories(zenohpico_shared PUBLIC ${_zp_build_interface_platform_include_dirs}) endif() endif() list(REMOVE_DUPLICATES _zp_platform_compile_definitions) foreach(_zp_platform_definition IN LISTS _zp_platform_compile_definitions) pico_add_compile_definition(${_zp_platform_definition}) endforeach() list(REMOVE_DUPLICATES _zp_platform_compile_options) if(NOT "${_zp_platform_compile_options}" STREQUAL "") if(PICO_STATIC) target_compile_options(zenohpico_static PRIVATE ${_zp_platform_compile_options}) endif() if(PICO_SHARED) target_compile_options(zenohpico_shared PRIVATE ${_zp_platform_compile_options}) endif() endif() list(REMOVE_DUPLICATES _zp_platform_link_libraries) foreach(_zp_platform_link_library IN LISTS _zp_platform_link_libraries) pico_target_link_library("${_zp_platform_link_library}") endforeach() if(CHECK_THREADS) pico_target_link_library(Threads::Threads) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") pico_target_link_library(rt) endif() # # Build tests, examples, installation only when project is root # if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) option(PACKAGING "Use option on Linux to produce Debian and RPM packages." OFF) option(BUILD_EXAMPLES "Use this to also build the examples." ON) option(BUILD_TOOLS "Use this to also build the tools." OFF) option(BUILD_TESTING "Use this to also build tests." ON) option(BUILD_INTEGRATION "Use this to also build integration tests." OFF) option(ASAN "Enable AddressSanitizer." OFF) message(STATUS "Produce Debian and RPM packages: ${PACKAGING}") message(STATUS "Build examples: ${BUILD_EXAMPLES}") message(STATUS "Build tools: ${BUILD_TOOLS}") message(STATUS "Build tests: ${BUILD_TESTING}") message(STATUS "Build integration: ${BUILD_INTEGRATION}") message(STATUS "AddressSanitizer: ${ASAN}") set(PICO_LIBS "") if(PICO_STATIC) list(APPEND PICO_LIBS zenohpico_static) endif() if(PICO_SHARED) list(APPEND PICO_LIBS zenohpico_shared) endif() set(_zp_required_dependency_targets "") if(PICO_STATIC) list(APPEND _zp_required_dependency_targets ${_zp_platform_imported_targets}) endif() list(REMOVE_DUPLICATES _zp_required_dependency_targets) zp_collect_target_packages(ZP_PACKAGE_DEPENDENCY_PACKAGES ${_zp_required_dependency_targets}) install(TARGETS ${PICO_LIBS} EXPORT zenohpicoTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin LIBRARY COMPONENT Runtime ARCHIVE COMPONENT Dev RUNTIME COMPONENT Runtime ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/zenoh-pico.h DESTINATION include COMPONENT Headers ) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/zenoh-pico DESTINATION include COMPONENT Headers PATTERN "include/zenoh-pico/config.h" EXCLUDE ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/zenoh-pico/config.h DESTINATION include/zenoh-pico COMPONENT Headers ) if(BUILD_SHARED_LIBS) set(LIBNAME ${CMAKE_SHARED_LIBRARY_PREFIX}${Libname}${CMAKE_SHARED_LIBRARY_SUFFIX}) else() set(LIBNAME ${CMAKE_STATIC_LIBRARY_PREFIX}${Libname}${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/zenohpico") # Generate Config.cmake configure_package_config_file( "PackageConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/zenohpicoConfig.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") # Generate Version.cmake write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/zenohpicoConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/zenohpicoConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/zenohpicoConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" CONFIGURATIONS ${configurations} COMPONENT Dev) # Generate Targets.cmake install( EXPORT zenohpicoTargets NAMESPACE zenohpico:: DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" COMPONENT Dev) if(UNIX) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/zenohpico.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/zenohpico.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zenohpico.pc" CONFIGURATIONS Release RelWithDebInfo DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT Dev) endif() if(ASAN AND NOT MSVC) add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) endif() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() if(UNIX OR MSVC) if(BUILD_TOOLS) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tools) add_executable(z_keyexpr_canonizer ${PROJECT_SOURCE_DIR}/tools/z_keyexpr_canonizer.c) target_link_libraries(z_keyexpr_canonizer zenohpico::lib) endif() if(BUILD_TESTING AND CMAKE_C_STANDARD MATCHES "11") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") add_executable(z_data_struct_test ${PROJECT_SOURCE_DIR}/tests/z_data_struct_test.c) add_executable(z_channels_test ${PROJECT_SOURCE_DIR}/tests/z_channels_test.c) add_executable(z_collections_test ${PROJECT_SOURCE_DIR}/tests/z_collections_test.c) add_executable(z_endpoint_test ${PROJECT_SOURCE_DIR}/tests/z_endpoint_test.c) add_executable(z_iobuf_test ${PROJECT_SOURCE_DIR}/tests/z_iobuf_test.c) add_executable(z_msgcodec_test ${PROJECT_SOURCE_DIR}/tests/z_msgcodec_test.c) add_executable(z_keyexpr_test ${PROJECT_SOURCE_DIR}/tests/z_keyexpr_test.c) add_executable(z_api_null_drop_test ${PROJECT_SOURCE_DIR}/tests/z_api_null_drop_test.c) add_executable(z_api_double_drop_test ${PROJECT_SOURCE_DIR}/tests/z_api_double_drop_test.c) add_executable(z_test_fragment_tx ${PROJECT_SOURCE_DIR}/tests/z_test_fragment_tx.c) add_executable(z_test_fragment_rx ${PROJECT_SOURCE_DIR}/tests/z_test_fragment_rx.c) add_executable(z_perf_tx ${PROJECT_SOURCE_DIR}/tests/z_perf_tx.c) add_executable(z_perf_rx ${PROJECT_SOURCE_DIR}/tests/z_perf_rx.c) add_executable(z_bytes_test ${PROJECT_SOURCE_DIR}/tests/z_bytes_test.c) add_executable(z_api_bytes_test ${PROJECT_SOURCE_DIR}/tests/z_api_bytes_test.c) add_executable(z_api_encoding_test ${PROJECT_SOURCE_DIR}/tests/z_api_encoding_test.c) add_executable(z_refcount_test ${PROJECT_SOURCE_DIR}/tests/z_refcount_test.c) add_executable(z_lru_cache_test ${PROJECT_SOURCE_DIR}/tests/z_lru_cache_test.c) add_executable(z_test_peer_unicast ${PROJECT_SOURCE_DIR}/tests/z_test_peer_unicast.c) add_executable(z_test_peer_multicast ${PROJECT_SOURCE_DIR}/tests/z_test_peer_multicast.c) add_executable(z_utils_test ${PROJECT_SOURCE_DIR}/tests/z_utils_test.c) add_executable(z_tls_test ${PROJECT_SOURCE_DIR}/tests/z_tls_test.c) add_executable(z_tls_config_test ${PROJECT_SOURCE_DIR}/tests/z_tls_config_test.c) add_executable(z_condvar_wait_until_test ${PROJECT_SOURCE_DIR}/tests/z_condvar_wait_until_test.c) add_executable(z_sync_group_test ${PROJECT_SOURCE_DIR}/tests/z_sync_group_test.c) add_executable(z_cancellation_token_test ${PROJECT_SOURCE_DIR}/tests/z_cancellation_token_test.c) add_executable(z_local_loopback_test ${PROJECT_SOURCE_DIR}/tests/z_local_loopback_test.c) add_executable(z_open_test ${PROJECT_SOURCE_DIR}/tests/z_open_test.c) add_executable(z_json_encoder_test ${PROJECT_SOURCE_DIR}/tests/z_json_encoder_test.c) add_executable(z_executor_test ${PROJECT_SOURCE_DIR}/tests/z_executor_test.c) add_executable(z_background_executor_test ${PROJECT_SOURCE_DIR}/tests/z_background_executor_test.c) add_executable(z_hashmap_test ${PROJECT_SOURCE_DIR}/tests/z_hashmap_test.c) add_executable(z_pqueue_test ${PROJECT_SOURCE_DIR}/tests/z_pqueue_test.c) add_executable(z_test_fragment_decode_error_transport_zbuf ${PROJECT_SOURCE_DIR}/tests/z_test_fragment_decode_error_transport_zbuf.c) target_link_libraries(z_data_struct_test zenohpico::lib) target_link_libraries(z_channels_test zenohpico::lib) target_link_libraries(z_collections_test zenohpico::lib) target_link_libraries(z_endpoint_test zenohpico::lib) target_link_libraries(z_iobuf_test zenohpico::lib) target_link_libraries(z_msgcodec_test zenohpico::lib) target_link_libraries(z_keyexpr_test zenohpico::lib) target_link_libraries(z_api_null_drop_test zenohpico::lib) target_link_libraries(z_api_double_drop_test zenohpico::lib) target_link_libraries(z_test_fragment_tx zenohpico::lib) target_link_libraries(z_test_fragment_rx zenohpico::lib) target_link_libraries(z_perf_tx zenohpico::lib) target_link_libraries(z_perf_rx zenohpico::lib) target_link_libraries(z_bytes_test zenohpico::lib) target_link_libraries(z_api_bytes_test zenohpico::lib) target_link_libraries(z_api_encoding_test zenohpico::lib) target_link_libraries(z_refcount_test zenohpico::lib) target_link_libraries(z_lru_cache_test zenohpico::lib) target_link_libraries(z_test_peer_unicast zenohpico::lib) target_link_libraries(z_test_peer_multicast zenohpico::lib) target_link_libraries(z_utils_test zenohpico::lib) target_link_libraries(z_tls_test zenohpico::lib) target_link_libraries(z_tls_config_test zenohpico::lib) target_link_libraries(z_condvar_wait_until_test zenohpico::lib) target_link_libraries(z_sync_group_test zenohpico::lib) target_link_libraries(z_cancellation_token_test zenohpico::lib) target_link_libraries(z_local_loopback_test zenohpico::lib) target_link_libraries(z_open_test zenohpico::lib) if(CHECK_THREADS) target_link_libraries(z_open_test Threads::Threads) endif() target_compile_definitions(z_local_loopback_test PRIVATE Z_TEST_HOOKS=1) if(PICO_SHARED) target_compile_definitions(${Libname}_shared PRIVATE Z_TEST_HOOKS=1) endif() if(PICO_STATIC) target_compile_definitions(${Libname}_static PRIVATE Z_TEST_HOOKS=1) endif() target_link_libraries(z_json_encoder_test zenohpico::lib) target_link_libraries(z_executor_test zenohpico::lib) target_link_libraries(z_background_executor_test zenohpico::lib) target_link_libraries(z_hashmap_test zenohpico::lib) target_link_libraries(z_pqueue_test zenohpico::lib) target_link_libraries(z_test_fragment_decode_error_transport_zbuf zenohpico::lib) target_compile_definitions(z_test_fragment_decode_error_transport_zbuf PRIVATE Z_TEST_HOOKS=1) configure_file(${PROJECT_SOURCE_DIR}/tests/modularity.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/modularity.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/raweth.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/raweth.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/fragment.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/fragment.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/single_thread.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/single_thread.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/attachment.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/attachment.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/no_router.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/no_router.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/memory_leak.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/memory_leak.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/connection_restore.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/connection_restore.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/routed_peer_client.py ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/routed_peer_client.py COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/valgrind.supp ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/valgrind.supp COPYONLY) if(UNIX) configure_file(${PROJECT_SOURCE_DIR}/tests/package_mylinux.sh.in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/package_mylinux.sh @ONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/package_myrtos.sh.in ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/package_myrtos.sh @ONLY) endif() enable_testing() add_test(z_data_struct_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_data_struct_test) add_test(z_channels_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_channels_test) add_test(z_collections_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_collections_test) add_test(z_endpoint_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_endpoint_test) add_test(z_iobuf_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_iobuf_test) add_test(z_msgcodec_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_msgcodec_test) add_test(z_keyexpr_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_keyexpr_test) add_test(z_api_null_drop_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_api_null_drop_test) add_test(z_api_double_drop_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_api_double_drop_test) add_test(z_bytes_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_bytes_test) add_test(z_api_bytes_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_api_bytes_test) add_test(z_api_encoding_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_api_encoding_test) add_test(z_refcount_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_refcount_test) add_test(z_lru_cache_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_lru_cache_test) add_test(z_utils_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_utils_test) add_test(z_tls_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_tls_test) add_test(z_tls_config_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_tls_config_test) add_test(z_condvar_wait_until_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_condvar_wait_until_test) add_test(z_sync_group_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_sync_group_test) add_test(z_cancellation_token_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_cancellation_token_test) add_test(z_local_loopback_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_local_loopback_test) add_test(z_open_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_open_test) add_test(z_json_encoder_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_json_encoder_test) add_test(z_executor_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_executor_test) add_test(z_background_executor_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_background_executor_test) add_test(z_hashmap_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_hashmap_test) add_test(z_pqueue_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_pqueue_test) add_test(z_test_fragment_decode_error_transport_zbuf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/z_test_fragment_decode_error_transport_zbuf) if(UNIX) add_test(z_package_mylinux_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/package_mylinux.sh) add_test(z_package_myrtos_configure_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/package_myrtos.sh) endif() endif() if(BUILD_INTEGRATION) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") if(CMAKE_C_STANDARD MATCHES "11") add_executable(z_client_test ${PROJECT_SOURCE_DIR}/tests/z_client_test.c) add_executable(z_api_alignment_test ${PROJECT_SOURCE_DIR}/tests/z_api_alignment_test.c) add_executable(z_wildcard_subscription_test ${PROJECT_SOURCE_DIR}/tests/z_wildcard_subscription_test.c) add_executable(z_api_liveliness_test ${PROJECT_SOURCE_DIR}/tests/z_api_liveliness_test.c) add_executable(z_api_matching_test ${PROJECT_SOURCE_DIR}/tests/z_api_matching_test.c) add_executable(z_api_source_info_test ${PROJECT_SOURCE_DIR}/tests/z_api_source_info_test.c) add_executable(z_api_connectivity_test ${PROJECT_SOURCE_DIR}/tests/z_api_connectivity_test.c) add_executable(z_api_queryable_test ${PROJECT_SOURCE_DIR}/tests/z_api_queryable_test.c) add_executable(z_api_cancellation_test ${PROJECT_SOURCE_DIR}/tests/z_api_cancellation_test.c) add_executable(z_api_local_queryable_test ${PROJECT_SOURCE_DIR}/tests/z_api_local_queryable_test.c) add_executable(z_api_local_subscriber_test ${PROJECT_SOURCE_DIR}/tests/z_api_local_subscriber_test.c) add_executable(z_api_admin_space_test ${PROJECT_SOURCE_DIR}/tests/z_api_admin_space_test.c) add_executable(z_multi_pubsub_test ${PROJECT_SOURCE_DIR}/tests/z_multi_pubsub_test.c) add_executable(z_multi_queryable_test ${PROJECT_SOURCE_DIR}/tests/z_multi_queryable_test.c) add_executable(z_api_batching_test ${PROJECT_SOURCE_DIR}/tests/z_api_batching_test.c) if(UNIX) add_executable(z_api_advanced_pubsub_test ${PROJECT_SOURCE_DIR}/tests/z_api_advanced_pubsub_test.c ${PROJECT_SOURCE_DIR}/tests/utils/tcp_proxy.c) add_executable(z_api_callback_drop_on_undeclare_test ${PROJECT_SOURCE_DIR}/tests/z_api_callback_drop_on_undeclare_test.c ${PROJECT_SOURCE_DIR}/tests/utils/tcp_proxy.c) add_compile_definitions(Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY) else() add_executable(z_api_advanced_pubsub_test ${PROJECT_SOURCE_DIR}/tests/z_api_advanced_pubsub_test.c) add_executable(z_api_callback_drop_on_undeclare_test ${PROJECT_SOURCE_DIR}/tests/z_api_callback_drop_on_undeclare_test.c) endif() target_link_libraries(z_client_test zenohpico::lib) target_link_libraries(z_api_alignment_test zenohpico::lib) target_link_libraries(z_wildcard_subscription_test zenohpico::lib) target_link_libraries(z_api_liveliness_test zenohpico::lib) target_link_libraries(z_api_matching_test zenohpico::lib) target_link_libraries(z_api_source_info_test zenohpico::lib) target_link_libraries(z_api_connectivity_test zenohpico::lib) target_link_libraries(z_api_queryable_test zenohpico::lib) target_link_libraries(z_api_advanced_pubsub_test zenohpico::lib) target_link_libraries(z_api_cancellation_test zenohpico::lib) target_link_libraries(z_api_local_queryable_test zenohpico::lib) target_link_libraries(z_api_local_subscriber_test zenohpico::lib) target_link_libraries(z_api_admin_space_test zenohpico::lib) target_link_libraries(z_multi_pubsub_test zenohpico::lib) target_link_libraries(z_multi_queryable_test zenohpico::lib) target_link_libraries(z_api_callback_drop_on_undeclare_test zenohpico::lib) target_link_libraries(z_api_batching_test zenohpico::lib) configure_file(${PROJECT_SOURCE_DIR}/tests/routed.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/routed.sh COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/tls_verify.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tls_verify.sh COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/tests/api.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh COPYONLY) enable_testing() if(Z_FEATURE_LINK_TLS) add_test(z_client_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/routed.sh z_client_test --enable-tls) add_test(z_tls_verify_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/tls_verify.sh z_client_test) else() add_test(z_client_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/routed.sh z_client_test) endif() add_test(z_api_alignment_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_alignment_test) add_test(z_wildcard_subscription_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_wildcard_subscription_test) add_test(z_api_liveliness_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_liveliness_test) add_test(z_api_cancellation_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_cancellation_test) add_test(z_api_matching_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_matching_test) add_test(z_api_source_info_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_source_info_test) add_test(z_api_connectivity_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_connectivity_test) add_test(z_api_queryable_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_queryable_test) add_test(z_api_advanced_pubsub_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_advanced_pubsub_test) add_test(z_api_local_queryable_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_local_queryable_test) add_test(z_api_local_subscriber_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_local_subscriber_test) add_test(z_api_admin_space_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_admin_space_test) add_test(z_multi_pubsub_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_multi_pubsub_test) add_test(z_multi_queryable_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_multi_queryable_test) add_test(z_api_callback_drop_on_undeclare_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_callback_drop_on_undeclare_test) add_test(z_api_batching_test bash ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/api.sh z_api_batching_test) endif() endif() endif() # For packaging if(PACKAGING) set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/packages") set(CPACK_COMPONENTS_ALL Runtime Headers Dev) set(CPACK_COMPONENT_RUNTIME_GROUP "lib") set(CPACK_COMPONENT_HEADERS_GROUP "dev") set(CPACK_COMPONENT_DEV_GROUP "dev") set(CPACK_COMPONENT_HEADERS_DEPENDS Runtime) set(CPACK_COMPONENT_DEV_DEPENDS Runtime) set(CPACK_PACKAGE_CHECKSUM MD5) set(CPACK_PACKAGE_VENDOR "The Eclipse Foundation") if(NOT CPACK_PACKAGE_VERSION) set(SEM_VER "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") if(PROJECT_VERSION_TWEAK STREQUAL "") set(CPACK_PACKAGE_VERSION ${SEM_VER}) elseif(PROJECT_VERSION_TWEAK EQUAL 0) set(CPACK_PACKAGE_VERSION "${SEM_VER}+dev-1") elseif(PROJECT_VERSION_TWEAK GREATER 0) set(CPACK_PACKAGE_VERSION "${SEM_VER}+pre.${PROJECT_VERSION_TWEAK}-1") endif() endif() set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "The C client library for Eclipse zenoh targeting pico devices") set(CPACK_COMPONENT_HEADERS_DESCRIPTION "${CPACK_COMPONENT_LIB_DESCRIPTION} - headers") set(CPACK_COMPONENT_DEV_DESCRIPTION "${CPACK_COMPONENT_LIB_DESCRIPTION} - config files") # Sources package set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-src-${project_version}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") if(PACKAGING MATCHES "DEB") if(NOT DEBARCH) set(DEBARCH ${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}) endif() if(CPACK_GENERATOR) set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB") else() set(CPACK_GENERATOR "DEB") endif() # DEB package set(CPACK_DEBIAN_PACKAGE_MAINTAINER "ZettaScale Zenoh Team, ") set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEBARCH}) set(CPACK_DEB_COMPONENT_INSTALL ON) set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) set(CPACK_DEBIAN_LIB_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}") set(CPACK_DEBIAN_LIB_PACKAGE_DEPENDS "libc6 (>=2.12)") set(CPACK_DEBIAN_DEV_PACKAGE_NAME "lib${CPACK_PACKAGE_NAME}-dev") set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "${CPACK_DEBIAN_LIB_PACKAGE_NAME} (=${CPACK_PACKAGE_VERSION})") endif() if(PACKAGING MATCHES "RPM") if(NOT RPMARCH) set(RPMARCH ${CMAKE_SYSTEM_PROCESSOR}) endif() if(CPACK_GENERATOR) set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") else() set(CPACK_GENERATOR "RPM") endif() # RPM package set(CPACK_RPM_PACKAGE_ARCHITECTURE ${RPMARCH}) set(CPACK_RPM_COMPONENT_INSTALL ON) set(CPACK_RPM_FILE_NAME RPM-DEFAULT) set(CPACK_RPM_LIB_PACKAGE_NAME ${CPACK_PACKAGE_NAME}) # avoid "-lib" suffix for "lib" package set(CPACK_RPM_DEV_PACKAGE_REQUIRES "${CPACK_RPM_LIB_PACKAGE_NAME} = ${CPACK_PACKAGE_VERSION}") endif() include(CPack) endif() endif() ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Eclipse zenoh Thanks for your interest in this project. ## Project description Eclipse zenoh provides is a stack designed to 1. minimize network overhead, 2. support extremely constrained devices, 3. supports devices with low duty-cycle by allowing the negotiation of data exchange modes and schedules, 4. provide a rich set of abstraction for distributing, querying and storing data along the entire system, and 5. provide extremely low latency and high throughput. * [https://projects.eclipse.org/projects/iot.zenoh](https://projects.eclipse.org/projects/iot.zenoh) ## Developer resources Information regarding source code management, builds, coding standards, and more. * [https://projects.eclipse.org/projects/iot.zenoh/developer](https://projects.eclipse.org/projects/iot.zenoh/developer) The project maintains the following source code repositories * [https://github.com/eclipse-zenoh](https://github.com/eclipse-zenoh) ## Eclipse Contributor Agreement Before your contribution can be accepted by the project team contributors must electronically sign the Eclipse Contributor Agreement (ECA). * [http://www.eclipse.org/legal/ECA.php](http://www.eclipse.org/legal/ECA.php) Commits that are provided by non-committers must have a Signed-off-by field in the footer indicating that the author is aware of the terms by which the contribution has been provided to the project. The non-committer must additionally have an Eclipse Foundation account and must have a signed Eclipse Contributor Agreement (ECA) on file. For more information, please see the Eclipse Committer Handbook: [https://www.eclipse.org/projects/handbook/#resources-commit](https://www.eclipse.org/projects/handbook/#resources-commit) ## Contact Contact the project developers via the project's "dev" list. * [https://accounts.eclipse.org/mailing-list/zenoh-dev](https://accounts.eclipse.org/mailing-list/zenoh-dev) Or via the Gitter channel. * [https://gitter.im/atolab/zenoh](https://gitter.im/atolab/zenoh) ================================================ FILE: CONTRIBUTORS.md ================================================ # Contributors to Eclipse zenoh These are the contributors to Eclipse zenoh (the initial contributors and the contributors listed in the Git log). | GitHub username | Name | | --------------- | -----------------------------| | kydos | Angelo Corsaro (ZettaScale) | | JEnoch | Julien Enoch (ZettaScale) | | OlivierHecart | Olivier Hécart (ZettaScale) | | gabrik | Gabriele Baldoni (ZettaScale) | | Mallets | Luca Cominardi (ZettaScale) | | IvanPaez | Ivan Paez (ZettaScale) | ================================================ FILE: GNUmakefile ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # .PHONY: test clean # Build type. This set the CMAKE_BUILD_TYPE variable. # Accepted values: Release, Debug, GCov BUILD_TYPE?=Release # Build examples. This sets the BUILD_EXAMPLES variable. # Accepted values: ON, OFF BUILD_EXAMPLES?=ON # Build testing. This sets the BUILD_TESTING variable. # Accepted values: ON, OFF BUILD_TESTING?=ON # Build integration tests. This sets the BUILD_INTEGRATION variable. # Accepted values: ON, OFF BUILD_INTEGRATION?=OFF # Build integration tests. This sets the BUILD_TOOLS variable. # Accepted values: ON, OFF BUILD_TOOLS?=OFF # Force the use of c99 standard. # Accepted values: ON, OFF FORCE_C99?=OFF # Enable AddressSanitizer. # Accepted values: ON, OFF ASAN?=OFF # Logging level. This sets the ZENOH_LOG variable. # Accepted values (empty string means no log): # ERROR/error # WARN/warn # INFO/info # DEBUG/debug # TRACE/trace ZENOH_LOG?="" # Feature config toggle # Accepted values: 0, 1 Z_FEATURE_UNSTABLE_API?=0 Z_FEATURE_CONNECTIVITY?=0 Z_FEATURE_MULTI_THREAD?=1 Z_FEATURE_PUBLICATION?=1 Z_FEATURE_ADVANCED_PUBLICATION?=0 Z_FEATURE_SUBSCRIPTION?=1 Z_FEATURE_ADVANCED_SUBSCRIPTION?=0 Z_FEATURE_QUERY?=1 Z_FEATURE_QUERYABLE?=1 Z_FEATURE_INTEREST?=1 Z_FEATURE_LIVELINESS?=1 Z_FEATURE_SCOUTING?=1 Z_FEATURE_MATCHING?=1 Z_FEATURE_UNICAST_TRANSPORT?=1 Z_FEATURE_MULTICAST_TRANSPORT?=1 Z_FEATURE_RAWETH_TRANSPORT?=0 Z_FEATURE_LOCAL_SUBSCRIBER?=0 Z_FEATURE_LOCAL_QUERYABLE?=0 Z_FEATURE_UNICAST_PEER?=1 Z_FEATURE_LINK_TLS?=0 Z_FEATURE_RX_CACHE?=0 Z_FEATURE_ADMIN_SPACE?=0 # Buffer sizes FRAG_MAX_SIZE?=300000 BATCH_UNICAST_SIZE?=65535 BATCH_MULTICAST_SIZE?=8192 # zenoh-pico/ directory ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) # Build directory BUILD_DIR=build # Crossbuild directory CROSSBUILD_TARGETS=linux-armv5 linux-armv6 linux-armv7 linux-armv7a linux-arm64 linux-mips linux-x86 linux-x64 CROSSBUILD_DIR=crossbuilds CROSSIMG_PREFIX=zenoh-pico_ # NOTES: # - ARM: old versions of dockcross/dockcross were creating some issues since they used an old GCC (4.8.3) which lacks (even using -std=gnu11) CMAKE_OPT=-DZENOH_DEBUG=$(ZENOH_DEBUG) -DZENOH_LOG=$(ZENOH_LOG) -DZENOH_LOG_PRINT=$(ZENOH_LOG_PRINT) -DBUILD_EXAMPLES=$(BUILD_EXAMPLES) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DBUILD_TESTING=$(BUILD_TESTING)\ -DZ_FEATURE_MULTI_THREAD=$(Z_FEATURE_MULTI_THREAD) -DZ_FEATURE_INTEREST=$(Z_FEATURE_INTEREST) -DZ_FEATURE_UNSTABLE_API=$(Z_FEATURE_UNSTABLE_API) -DZ_FEATURE_CONNECTIVITY=$(Z_FEATURE_CONNECTIVITY)\ -DZ_FEATURE_PUBLICATION=$(Z_FEATURE_PUBLICATION) -DZ_FEATURE_SUBSCRIPTION=$(Z_FEATURE_SUBSCRIPTION) -DZ_FEATURE_QUERY=$(Z_FEATURE_QUERY) -DZ_FEATURE_QUERYABLE=$(Z_FEATURE_QUERYABLE)\ -DZ_FEATURE_LIVELINESS=$(Z_FEATURE_LIVELINESS) -DZ_FEATURE_MATCHING=$(Z_FEATURE_MATCHING) -DZ_FEATURE_SCOUTING=$(Z_FEATURE_SCOUTING)\ -DZ_FEATURE_ADVANCED_PUBLICATION=$(Z_FEATURE_ADVANCED_PUBLICATION) -DZ_FEATURE_ADVANCED_SUBSCRIPTION=$(Z_FEATURE_ADVANCED_SUBSCRIPTION)\ -DZ_FEATURE_UNICAST_TRANSPORT=$(Z_FEATURE_UNICAST_TRANSPORT) -DZ_FEATURE_MULTICAST_TRANSPORT=$(Z_FEATURE_MULTICAST_TRANSPORT) -DZ_FEATURE_ADMIN_SPACE=$(Z_FEATURE_ADMIN_SPACE)\ -DZ_FEATURE_RAWETH_TRANSPORT=$(Z_FEATURE_RAWETH_TRANSPORT) -DZ_FEATURE_LOCAL_SUBSCRIBER=$(Z_FEATURE_LOCAL_SUBSCRIBER) -DZ_FEATURE_LOCAL_QUERYABLE=$(Z_FEATURE_LOCAL_QUERYABLE)\ -DFRAG_MAX_SIZE=$(FRAG_MAX_SIZE) -DBATCH_UNICAST_SIZE=$(BATCH_UNICAST_SIZE) -DZ_FEATURE_LINK_TLS=$(Z_FEATURE_LINK_TLS) -DZ_FEATURE_RX_CACHE=$(Z_FEATURE_RX_CACHE)\ -DBATCH_MULTICAST_SIZE=$(BATCH_MULTICAST_SIZE) -DZ_FEATURE_UNICAST_PEER=$(Z_FEATURE_UNICAST_PEER) -DASAN=$(ASAN) -DBUILD_INTEGRATION=$(BUILD_INTEGRATION) -DBUILD_TOOLS=$(BUILD_TOOLS) -DBUILD_SHARED_LIBS=$(BUILD_SHARED_LIBS) -H. ifeq ($(FORCE_C99), ON) CMAKE_OPT += -DCMAKE_C_STANDARD=99 endif all: make $(BUILD_DIR)/Makefile: mkdir -p $(BUILD_DIR) echo $(CMAKE_OPT) cmake $(CMAKE_OPT) -B $(BUILD_DIR) make: $(BUILD_DIR)/Makefile cmake --build $(BUILD_DIR) install: $(BUILD_DIR)/Makefile cmake --install $(BUILD_DIR) test: make if [ "$(shell uname -s)" = "Darwin" ]; then \ sudo ctest --verbose --test-dir build; \ else \ ctest --verbose --test-dir build; \ fi crossbuilds: $(CROSSBUILD_TARGETS) DOCKER_OK := $(shell docker version 2> /dev/null) check-docker: ifndef DOCKER_OK $(error "Docker is not available. Please install Docker") endif crossbuild: check-docker @echo "FROM dockcross/$(CROSSIMG)\nRUN apt-get update && apt-get -y install rpm" | docker build -t $(CROSSIMG_PREFIX)$(CROSSIMG) - docker run --rm -v $(ROOT_DIR):/workdir -w /workdir $(CROSSIMG_PREFIX)$(CROSSIMG) bash -c "\ cmake $(CMAKE_OPT) -DCPACK_PACKAGE_NAME=$(PACKAGE_NAME) -DPACKAGING=DEB,RPM -DDEBARCH=$(DEBARCH) -DRPMARCH=$(RPMARCH) -B$(CROSSBUILD_DIR)/$(CROSSIMG) && \ make VERBOSE=1 -C$(CROSSBUILD_DIR)/$(CROSSIMG) all package" docker rmi $(CROSSIMG_PREFIX)$(CROSSIMG) linux-armv5: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=arm RPMARCH=arm make crossbuild linux-armv6: PACKAGE_NAME="zenohpico-armv6" CROSSIMG=$@ DEBARCH=arm RPMARCH=arm make crossbuild linux-armv7: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=armhf RPMARCH=armhf make crossbuild linux-armv7a: PACKAGE_NAME="zenohpico-armv7a" CROSSIMG=$@ DEBARCH=armhf RPMARCH=armhf make crossbuild linux-arm64: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=arm64 RPMARCH=aarch64 make crossbuild linux-mips: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=mips RPMARCH=mips make crossbuild linux-x86: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=i386 RPMARCH=x86 make crossbuild linux-x64: PACKAGE_NAME="zenohpico" CROSSIMG=$@ DEBARCH=amd64 RPMARCH=x86_64 make crossbuild clean: rm -fr $(BUILD_DIR) rm -rf $(CROSSBUILD_DIR) ================================================ FILE: LICENSE ================================================ apache-2.0 epl-2.0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the pur_poses of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the pur_poses of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the pur_poses of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the pur_pose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational pur_poses only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. OR Eclipse Public License - v 2.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution "originates" from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. "Contributor" means any person or entity that Distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions Distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. "Derivative Works" shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. "Modified Works" shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for pur_poses of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. "Distribute" means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. "Source Code" means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. "Secondary License" means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 3. REQUIREMENTS 3.1 If a Contributor Distributes the Program in any form, then: a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular pur_pose; ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 3.2 When the Program is Distributed as Source Code: a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and b) a copy of this Agreement must be included with each copy of the Program. 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability ("notices") contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. Exhibit A - Form of Secondary Licenses Notice "This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}." Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. ================================================ FILE: NOTICE.md ================================================ # Notices for Eclipse zenoh-pico This content is produced and maintained by the Eclipse zenoh project. * Project home: [https://projects.eclipse.org/projects/iot.zenoh](https://projects.eclipse.org/projects/iot.zenoh) ## Trademarks Eclipse zenoh is trademark of the Eclipse Foundation. Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. ## Copyright All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. ## Declared Project Licenses This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at [http://www.eclipse.org/legal/epl-2.0](http://www.eclipse.org/legal/epl-2.0), or the Apache License, Version 2.0 which is available at [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0). SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 ## Source Code The project maintains the following source code repositories: * [https://github.com/eclipse-zenoh/zenoh.git](https://github.com/eclipse-zenoh/zenoh.git) * [https://github.com/eclipse-zenoh/zenoh-pico.git](https://github.com/eclipse-zenoh/zenoh-pico.git) * [https://github.com/eclipse-zenoh/zenoh-java.git](https://github.com/eclipse-zenoh/zenoh-java.git) * [https://github.com/eclipse-zenoh/zenoh-go.git](https://github.com/eclipse-zenoh/zenoh-go.git) * [https://github.com/eclipse-zenoh/zenoh-python.git](https://github.com/eclipse-zenoh/zenoh-python.git) ## Third-party Content *To be completed...* ================================================ FILE: PackageConfig.cmake.in ================================================ include(CMakeFindDependencyMacro) set(ZENOHPICO_FEATURE_UNSTABLE_API @Z_FEATURE_UNSTABLE_API@) set(ZENOHPICO_FEATURE_CONNECTIVITY @Z_FEATURE_CONNECTIVITY@) set(ZENOHPICO_FEATURE_MULTI_THREAD @Z_FEATURE_MULTI_THREAD@) set(ZENOHPICO_FEATURE_PUBLICATION @Z_FEATURE_PUBLICATION@) set(ZENOHPICO_FEATURE_ADVANCED_PUBLICATION @Z_FEATURE_ADVANCED_PUBLICATION@) set(ZENOHPICO_FEATURE_SUBSCRIPTION @Z_FEATURE_SUBSCRIPTION@) set(ZENOHPICO_FEATURE_ADVANCED_SUBSCRIPTION @Z_FEATURE_ADVANCED_SUBSCRIPTION@) set(ZENOHPICO_FEATURE_QUERY @Z_FEATURE_QUERY@) set(ZENOHPICO_FEATURE_QUERYABLE @Z_FEATURE_QUERYABLE@) set(ZENOHPICO_FEATURE_RAWETH_TRANSPORT @Z_FEATURE_RAWETH_TRANSPORT@) set(ZENOHPICO_FEATURE_INTEREST @Z_FEATURE_INTEREST@) set(ZENOHPICO_FEATURE_LIVELINESS @Z_FEATURE_LIVELINESS@) set(ZENOHPICO_FEATURE_BATCHING @Z_FEATURE_BATCHING@) if(@CHECK_THREADS@) find_dependency(Threads REQUIRED) endif() set(_zenohpico_dependency_packages "@ZP_PACKAGE_DEPENDENCY_PACKAGES@") foreach(_zenohpico_dependency_package IN LISTS _zenohpico_dependency_packages) if(NOT _zenohpico_dependency_package STREQUAL "") find_dependency(${_zenohpico_dependency_package} CONFIG REQUIRED) endif() endforeach() include("${CMAKE_CURRENT_LIST_DIR}/zenohpicoTargets.cmake") if(@PICO_SHARED@) add_library(zenohpico::shared ALIAS zenohpico::zenohpico_shared) endif() if(@PICO_STATIC@) add_library(zenohpico::static ALIAS zenohpico::zenohpico_static) endif() if(@BUILD_SHARED_LIBS@) add_library(zenohpico::lib ALIAS zenohpico::zenohpico_shared) else() add_library(zenohpico::lib ALIAS zenohpico::zenohpico_static) endif() ================================================ FILE: README.md ================================================ ![Build](https://github.com/eclipse-zenoh/zenoh-pico/workflows/build/badge.svg) ![integration](https://github.com/eclipse-zenoh/zenoh-pico/workflows/integration/badge.svg) [![Documentation Status](https://readthedocs.org/projects/zenoh-pico/badge/?version=latest)](https://zenoh-pico.readthedocs.io/en/latest/?badge=latest) [![Discussion](https://img.shields.io/badge/discussion-on%20github-blue)](https://github.com/eclipse-zenoh/roadmap/discussions) [![Discord](https://img.shields.io/badge/chat-on%20discord-blue)](https://discord.gg/2GJ958VuHs) [![License](https://img.shields.io/badge/License-EPL%202.0-blue)](https://choosealicense.com/licenses/epl-2.0/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) # Eclipse Zenoh The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute. Zenoh (pronounce _/zeno/_) unifies data in motion, data at rest, and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks. Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information. ------------------------------- # Zenoh-Pico: native C library for constrained devices zenoh-pico is the [Eclipse zenoh](http://zenoh.io) implementation that targets constrained devices, offering a native C API. It is fully compatible with its main [Rust Zenoh implementation](https://github.com/eclipse-zenoh/zenoh), providing a lightweight implementation of most functionalities. Currently, zenoh-pico provides support for the following (RT)OSs and protocols: | **(RT)OS** | **Transport Layer** | **Network Layer** | **Data Link Layer** | |:---------------------:|:--------------------------------:|:-------------------:|:--------------------------------------------------:| | **Unix** | UDP (unicast and multicast), TCP | IPv4, IPv6, 6LoWPAN | WiFi, Ethernet, Thread | | **Windows** | UDP (unicast and multicast), TCP | IPv4, IPv6 | WiFi, Ethernet | | **Zephyr** | UDP (unicast and multicast), TCP | IPv4, IPv6, 6LoWPAN | WiFi, Ethernet, Thread, Serial | | **Arduino** | UDP (unicast and multicast), TCP | IPv4, IPv6 | WiFi, Ethernet, Bluetooth (Serial profile), Serial | | **ESP-IDF** | UDP (unicast and multicast), TCP | IPv4, IPv6 | WiFi, Ethernet, Serial | | **MbedOS** | UDP (unicast and multicast), TCP | IPv4, IPv6 | WiFi, Ethernet, Serial | | **OpenCR** | UDP (unicast and multicast), TCP | IPv4 | WiFi | | **Emscripten** | Websocket | IPv4, IPv6 | WiFi, Ethernet | | **FreeRTOS-Plus-TCP** | UDP (unicast), TCP | IPv4 | Ethernet | | **Raspberry Pi Pico** | UDP (unicast and multicast), TCP | IPv4 | WiFi (for "W" version), Serial, USB (CDC) | Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information. ------------------------------- ## 1. How to install it The Eclipse zenoh-pico library is available as **Debian**, **RPM**, and **tgz** packages in the [Eclipse zenoh-pico download area](https://download.eclipse.org/zenoh/zenoh-pico/). Those packages are built using manylinux2010 x86-32 and x86-64 for compatibility with most Linux platforms. There are two kind of packages: - **libzenohpico**: only contains the library file (.so) - **libzenohpico-dev**: contains the zenoh-pico header files for development and depends on the _libzenohpico_ package For other platforms - like RTOS for embedded systems / microcontrollers -, you will need to clone and build the sources. Check [below](#2. how-to-build-it) for more details. ------------------------------- ## 2. How to build it For platform profiles and adding a new platform, see [docs/platforms.rst](docs/platforms.rst). ### 2.1 Unix Environments To build the **zenoh-pico** library, you need to ensure that [cmake](https://cmake.org) is available on your platform -- if not please install it. Once the [cmake](https://cmake.org) dependency is satisfied, just do the following for **CMake** version 3 and higher: -- CMake version 3 and higher -- ```bash cd /path/to/zenoh-pico make make install # on Linux use **sudo** ``` If you want to build with debug symbols, set the `BUILD_TYPE=Debug`environment variable before to run make: ```bash cd /path/to/zenoh-pico BUILD_TYPE=Debug make make install # on Linux use **sudo** ``` For those that still have **CMake** version 2.8, do the following commands: ```bash cd /path/to/zenoh-pico mkdir build cd build cmake -DCMAKE_BUILD_TYPE=Release ../cmake-2.8 make make install # on Linux use **sudo** ``` ### 2.2. Real Time Operating System (RTOS) for Embedded Systems and Microcontrollers In order to manage and ease the process of building and deploying into a a variety of platforms and frameworks for embedded systems and microcontrollers, [PlatformIO](https://platformio.org) can be used as a supporting platform. Once the PlatformIO dependency is satisfied, follow the steps below for the tested micro controllers. #### 2.2.1. Zephyr Note: tested with reel_board, nucleo-f767zi, nucleo-f420zi, and nRF52840 boards. A typical PlatformIO project for Zephyr framework must have the following structure: ```bash project_dir ├── include ├── lib ├── src │ └── main.c ├── zephyr │ ├── prj.conf │ └── CMakeLists.txt └── platformio.ini ``` To initialize this project structure, execute the following commands: ```bash mkdir -p /path/to/project_dir cd /path/to/project_dir platformio init -b reel_board platformio run ``` Include the CMakelist.txt and prj.conf in the project_dir/zephyr folder as shown in the structure above, ```bash cp /path/to/zenoh-pico/docs/zephyr/reel_board/CMakelists.txt /path/to/project_dir/zephyr/ cp /path/to/zenoh-pico/docs/zephyr/reel_board/prj.conf /path/to/project_dir/zephyr/ ``` and add zenoh-pico as a library by doing: ```bash ln -s /path/to/zenoh-pico /path/to/project_dir/lib/zenoh-pico ``` or just include the following line in platformio.ini: ```ini lib_deps = https://github.com/eclipse-zenoh/zenoh-pico ``` Finally, your code should go into project_dir/src/main.c. Check the examples provided in examples directory. To build and upload the code into the board, run the following command: ```bash platformio run platformio run -t upload ``` #### 2.2.2. Arduino Note: tested with az-delivery-devkit-v4 ESP32 board A typical PlatformIO project for Arduino framework must have the following structure: ```bash project_dir ├── include ├── lib ├── src │ └── main.ino └── platformio.ini ``` To initialize this project structure, execute the following commands: ```bash mkdir -p /path/to/project_dir cd /path/to/project_dir platformio init -b az-delivery-devkit-v4 platformio run ``` Add zenoh-pico as a library by doing: ```bash ln -s /path/to/zenoh-pico /path/to/project_dir/lib/zenoh-pico ``` or just include the following line in platformio.ini: ```ini lib_deps = https://github.com/eclipse-zenoh/zenoh-pico ``` Finally, your code should go into project_dir/src/main.ino. Check the examples provided in examples directory. To build and upload the code into the board, run the following command: ```bash platformio run platformio run -t upload ``` #### 2.2.3. ESP-IDF Note: tested with az-delivery-devkit-v4 ESP32 board A typical PlatformIO project for ESP-IDF framework must have the following structure: ```bash project_dir ├── include ├── lib ├── src | ├── CMakeLists.txt │ └── main.c ├── CMakeLists.txt └── platformio.ini ``` To initialize this project structure, execute the following commands: ```bash mkdir -p /path/to/project_dir cd /path/to/project_dir platformio init -b az-delivery-devkit-v4 platformio run ``` Add zenoh-pico as a library by doing: ```bash ln -s /path/to/zenoh-pico /path/to/project_dir/lib/zenoh-pico ``` or just include the following line in platformio.ini: ```ini lib_deps = https://github.com/eclipse-zenoh/zenoh-pico ``` Finally, your code should go into project_dir/src/main.ino. Check the examples provided in examples directory. To build and upload the code into the board, run the following command: ```bash platformio run platformio run -t upload ``` #### 2.2.4. MbedOS Note: tested with nucleo-f747zi and nucleo-f429zi boards A typical PlatformIO project for MbedOS framework must have the following structure: ```bash project_dir ├── include ├── src │ └── main.ino └── platformio.ini ``` To initialize this project structure, execute the following commands: ```bash mkdir -p /path/to/project_dir cd /path/to/project_dir platformio init -b az-delivery-devkit-v4 platformio run ``` Add zenoh-pico as a library by doing: ```bash ln -s /path/to/zenoh-pico /path/to/project_dir/lib/zenoh-pico ``` or just include the following line in platformio.ini: ```ini lib_deps = https://github.com/eclipse-zenoh/zenoh-pico ``` Finally, your code should go into project_dir/src/main.ino. Check the examples provided in examples directory. To build and upload the code into the board, run the following command: ```bash platformio run platformio run -t upload ``` #### 2.2.5. OpenCR Note: tested with ROBOTIS OpenCR 1.0 board A typical PlatformIO project for OpenCR framework must have the following structure: ```bash project_dir ├── include ├── lib ├── src │ └── main.ino └── platformio.ini ``` Note: to add support for OpenCR in PlatformIO, follow the steps presented in our [blog](https://zenoh.io/blog/2022-02-08-dragonbot/). To initialize this project structure, execute the following commands: ```bash mkdir -p /path/to/project_dir cd /path/to/project_dir platformio init -b opencr platformio run ``` Add zenoh-pico as a library by doing: ```bash ln -s /path/to/zenoh-pico /path/to/project_dir/lib/zenoh-pico ``` or just include the following line in platformio.ini: ```ini lib_deps = https://github.com/eclipse-zenoh/zenoh-pico ``` Finally, your code should go into project_dir/src/main.ino. Check the examples provided in examples directory. To build and upload the code into the board, run the following command: ```bash platformio run platformio run -t upload ``` #### 2.2.6. Raspberry Pi Pico Note: tested with `Raspberry Pi Pico W` and `Raspberry Pi Pico 2 W` boards Ensure your system has the necessary tools and libraries installed. Run the following commands: ```bash sudo apt update sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential g++ libstdc++-arm-none-eabi-newlib ``` Set up the Raspberry Pi Pico SDK by cloning the repository: ```bash export PICO_SDK_PATH=$HOME/src/pico-sdk mkdir -p $PICO_SDK_PATH cd $PICO_SDK_PATH git clone https://github.com/raspberrypi/pico-sdk.git . git submodule update --init ``` Clone the FreeRTOS Kernel repository for the project: ```bash export FREERTOS_KERNEL_PATH=$HOME/src/FreeRTOS-Kernel/ mkdir -p $FREERTOS_KERNEL_PATH cd $FREERTOS_KERNEL_PATH git clone https://github.com/FreeRTOS/FreeRTOS-Kernel.git . git submodule update --init ``` Setup and build the examples: - `PICO_BOARD` - Pico board type: pico, pico_w, pico2, pico2_w (default: pico_w) - `WIFI_SSID` - Wi-Fi network SSID - `WIFI_PASSWORD` - Wi-Fi password - `ZENOH_CONFIG_MODE` - client or peer mode (default: client) - `ZENOH_CONFIG_CONNECT` - connect endpoint (only for client mode, optional) - `ZENOH_CONFIG_LISTEN` - listen endpoint (only for peer mode, optional) ```bash cd examples/rpi_pico cmake -Bbuild -DPICO_BOARD="pico" -DWIFI_SSID=wifi_network_ssid -DWIFI_PASSWORD=wifi_network_password -DZENOH_CONFIG_MODE=[client|peer] -DZENOH_CONFIG_CONNECT=connect -DZENOH_CONFIG_LISTEN=listen cmake --build ./build ``` To flash the Raspberry Pi Pico board, connect it in bootloader mode (it will appear as a removable drive) and copy the generated .uf2 file onto it. **Serial connection**: To connect via UART specify pins or predefined device name and baud rate: e.g. ```bash -DZENOH_CONFIG_CONNECT="serial/0.1#baudrate=38400" -DZENOH_CONFIG_CONNECT="serial/uart1_0#baudrate=38400" ``` Valid PIN combinations and associated device names: | **PINS** | **Device name** | |---------:|:---------------:| | 0.1 | uart0_0 | | 4.5 | uart1_0 | | 8.9 | uart1_1 | | 12.13 | uart0_1 | | 16.17 | uart0_2 | **USB Serial connection (experemental)**: To enable this feature, zenoh-pico should be compiled with `Z_FEATURE_LINK_SERIAL_USB` and `Z_FEATURE_UNSTABLE_API` enabled. To connect via USB CDC, specify `usb` device: e.g. ```bash -DZENOH_CONFIG_CONNECT="serial/usb#baudrate=112500" ``` On the host Zenoh, specify the USB CDC device: e.g. ```bash zenohd -l serial//dev/ttyACM1#baudrate=112500 ``` #### 2.2.7. STM32 ThreadX 1. Create a new project using STMCubeIDE for your target MCU. 2. In CubeMX project configuration tool: - Add and enable AZURE-RTOS(threadx) Middleware. - Enable UART periphery, set up RX DMA in circular mode and enable UART global interrupt. - Move HAL_Tick to TIM peripheral for threadx to work with HAL. (SYS - Timebase source configuration) 3. Generate initialization code with CubeMX. 4. Clone zenoh-pico repository to your project folder (or submodule, copy files) 5. Add zenoh-pico/src to project source folders. Exclude any folders in platforms/* except common and threadx/stm32. 6. Add zenoh-pico/include to project include paths. 7. Edit zenoh-pico/include/config.h to exclude unsuported features or add compile flags 8. Add "hal.h" file to your project. This file should include your target hal headers (ex. `#include "stm32f4xx_hal.h"`) 9. Add defines to project: - `ZENOH_THREADX_STM32` - `ZENOH_HUART=huartx` (huart1 / huar2, etc...) - `ZENOH_THREADX_STM32_GEN_IRQ=0` (only if you want to write your own interrupt handler) 10. Exclude from build /Core/Src/app_threadx.c and replace with one of the z_*.c* files from zenoh-pico/examples/threadx_stm32/ 11. Set `TICK_INT_PRIORITY` in stm32xx_hal_conf.h to higher value than SysTick. (0 works ok for testing). 12. Set static bytepool size bigger than 25kB. 13. On host compile zenohd with serial support and run with: ```bash zenohd -l serial//dev/ttyACM0#baudrate=115200 ``` ## 3. Running the Examples The simplest way to run some of the example is to get a Docker image of the **zenoh** router (see [http://zenoh.io/docs/getting-started/quick-test/](http://zenoh.io/docs/getting-started/quick-test/)) and then to run the examples on your machine. ### 3.1. Starting the Zenoh Router Assuming you've pulled the Docker image of the **zenoh** router on a Linux host (to leverage UDP multicast scouting as explained [here](https://zenoh.io/docs/getting-started/quick-test/#run-zenoh-router-in-a-docker-container), then simply do: ```bash docker run --init --net host eclipse/zenoh:main ``` To see the zenoh manual page, simply do: ```bash docker run --init --net host eclipse/zenoh:main --help ``` :warning: **Please notice that the `--net host` option in Docker is restricted to Linux only.** The cause is that Docker doesn't support UDP multicast between a container and its host (see cases [moby/moby#23659](https://github.com/moby/moby/issues/23659), [moby/libnetwork#2397](https://github.com/moby/libnetwork/issues/2397) or [moby/libnetwork#552](https://github.com/moby/libnetwork/issues/552)). The only known way to make it work is to use the `--net host` option that is [only supported on Linux hosts](https://docs.docker.com/network/host/). ### 3.2. Basic Pub/Sub Example Assuming that (1) you are running the **zenoh** router, and (2) you are under the build directory, do: ```bash ./z_sub ``` And on another shell, do: ```bash ./z_pub ``` ### 3.3. Basic Queryable/Get Example Assuming you are running the **zenoh** router, do: ```bash ./z_queryable ``` And on another shell, do: ```bash ./z_get ``` ### 3.4. Basic Pub/Sub Example - P2P over UDP multicast Zenoh-Pico can also work in P2P mode over UDP multicast. This allows a Zenoh-Pico application to communicate directly with another Zenoh-Pico application without requiring a Zenoh Router. Assuming that (1) you are under the build directory, do: ```bash ./z_sub -m peer -l udp/224.0.0.123:7447#iface=lo0 ``` And on another shell, do: ```bash ./z_pub -m peer -l udp/224.0.0.123:7447#iface=lo0 ``` where `lo0` is the network interface you want to use for multicast communication. > [!WARNING] > Multicast communication does not perform any negotiation upon group joining. Because of that, it is important that all transport parameters are the same to make sure all your nodes in the system can communicate. > One common parameter to configure is the batch size since its default value depends on the actual platform when operating on multicast: > > - with zenoh-pico you can configure it via the `BATCH_MULTICAST_SIZE` build option (see [below](#error-when-opening-a-session-on-a-microcontroller)) > - with other Zenoh APIs, set the "transport/link/tx/batch_size" value in configuration file > > E.g., the batch size on Linux and Windows is 65535 bytes, on Mac OS X is 9216, anything else is 8192. ### 3.4. Basic Pub/Sub Example - Mixing Client and P2P communication To allow Zenoh-Pico unicast clients to talk to Zenoh-Pico multicast peers, as well as with any other Zenoh client/peer, you need to start a Zenoh Router that listens on both multicast and unicast: ```bash docker run --init --net host eclipse/zenoh:main -l udp/224.0.0.123:7447#iface=lo0 -l tcp/127.0.0.1:7447 ``` Assuming that (1) you are running the **zenoh** router as indicated above, and (2) you are under the build directory, do: ```bash ./z_sub -m client -e tcp/127.0.0.1:7447 ``` A subscriber will connect in client mode to the **zenoh** router over TCP unicast. And on another shell, do: ```bash ./z_pub -m peer -l udp/224.0.0.123:7447#iface=lo0 ``` A publisher will start publishing over UDP multicast and the **zenoh** router will take care of forwarding data from the Zenoh-Pico publisher to the Zenoh-Pico subscriber. ## Troubleshooting ### Activate debug logs By default debug logs are deactivated but if you're encountering issues they can help you finding the cause. To activate them you need to pass the build flag value: `-DZENOH_LOG=DEBUG` ### Error when opening a session on a microcontroller If you get an error when opening the session even though everything is setup correctly, it might be because the default buffer sizes are too large for the limited memory available on your system. The first thing to try is to reduce the values of the following configuration options (found in `CMakeLists.txt`): - BATCH_UNICAST_SIZE: The maximum size of a packet in client mode. - BATCH_MULTICAST_SIZE: The maximum size of a packet in peer mode. - FRAG_MAX_SIZE: The maximum size of a message that can be fragmented into multiple packets. Until you find values that suits both your app requirements and your system memory constraints. These values can also be passed directly as cmake args. For example, in a `platformio.ini` you might write: ```bash board_build.cmake_extra_args= -DBATCH_UNICAST_SIZE=1024 -DFRAG_MAX_SIZE=2048 ``` ================================================ FILE: ci/scripts/build-linux.bash ================================================ #!/usr/bin/env bash set -xeo pipefail # Repository readonly repo=${REPO:?input REPO is required} # Release number readonly version=${VERSION:?input VERSION is required} # Build target readonly target=${TARGET:?input TARGET is required} BUILD_SHARED_LIBS=ON BUILD_TYPE=RELEASE make "$target" readonly out=$GITHUB_WORKSPACE readonly repo_name=${repo#*/} readonly archive_lib=$out/$repo_name-$version-$target-standalone.zip readonly archive_deb=$out/$repo_name-$version-$target-debian.zip readonly archive_rpm=$out/$repo_name-$version-$target-rpm.zip readonly archive_examples=$out/$repo_name-$version-$target-examples.zip cd crossbuilds/"$target" zip -r "$archive_lib" lib cd - zip -r "$archive_lib" include cd crossbuilds/"$target"/packages zip -9 -j "$archive_deb" ./*.deb zip -9 -j "$archive_rpm" ./*.rpm cd - cd crossbuilds/"$target"/examples zip "$archive_examples" ./* cd - { echo "archive-lib=$(basename "$archive_lib")"; echo "archive-deb=$(basename "$archive_deb")"; echo "archive-rpm=$(basename "$archive_rpm")"; echo "archive-examples=$(basename "$archive_examples")"; } >> "$GITHUB_OUTPUT" ================================================ FILE: ci/scripts/build-macos.bash ================================================ #!/usr/bin/env bash set -xeo pipefail # Repository readonly repo=${REPO:?input REPO is required} # Release number readonly version=${VERSION:?input VERSION is required} BUILD_TYPE=RELEASE make readonly out=$GITHUB_WORKSPACE readonly repo_name=${repo#*/} readonly archive_lib=$out/$repo_name-$version-macos-x64.zip readonly archive_examples=$out/$repo_name-$version-macos-x64-examples.zip cd build zip -r "$archive_lib" lib cd .. zip -r "$archive_lib" include cd build/examples zip "$archive_examples" ./* cd .. { echo "archive-lib=$(basename "$archive_lib")"; echo "archive-examples=$(basename "$archive_examples")"; } >> "$GITHUB_OUTPUT" ================================================ FILE: ci/scripts/bump-and-tag.bash ================================================ #!/usr/bin/env bash set -xeo pipefail readonly live_run=${LIVE_RUN:-false} # Release number readonly version=${VERSION:?input VERSION is required} # Git actor name readonly git_user_name=${GIT_USER_NAME:?input GIT_USER_NAME is required} # Git actor email readonly git_user_email=${GIT_USER_EMAIL:?input GIT_USER_EMAIL is required} export GIT_AUTHOR_NAME=$git_user_name export GIT_AUTHOR_EMAIL=$git_user_email export GIT_COMMITTER_NAME=$git_user_name export GIT_COMMITTER_EMAIL=$git_user_email # Bump CMake project version printf '%s' "$version" > version.txt # Update the version field in library.json directly (it is no longer # generated by CMake). tmp=$(mktemp) jq --indent 4 --arg v "$version" '.version = $v' library.json > "$tmp" mv "$tmp" library.json # Refresh the in-tree copy of zenoh-pico.h so the release commit (and # the tag that points at it) is internally consistent. A short CMake # configure is enough — configure_file() writes the file into the # source tree as part of the normal configure step. cmake -S . -B build-bump -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF rm -rf build-bump git commit version.txt include/zenoh-pico.h library.json \ -m "chore: Bump version to $version" if [[ ${live_run} == true ]]; then git tag --force "$version" -m "v$version" git show-ref --tags git --no-pager log -10 git push --force origin git push --force origin "$version" else git --no-pager log -10 git push --force origin fi ================================================ FILE: ci/scripts/pre-build.bash ================================================ #!/usr/bin/env bash set -xeo pipefail # Extract cross-compilation targets from the Makefile crossbuild_targets=$(sed -n 's/^CROSSBUILD_TARGETS=\(.*\)/\1/p' GNUmakefile | head -n1) target_matrix="{\"target\": [\"${crossbuild_targets// /\",\"}\"]}" echo "build-linux-matrix=$target_matrix" >> "$GITHUB_OUTPUT" ================================================ FILE: cmake/helpers.cmake ================================================ # # Copyright (c) 2023 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # # # DO NOT add include_guard() to this file # It have to be reincluded multiple times on each 'include_project' call to restore # functions and macros definitions # # # Show VARIABLE = value on configuration stage # macro(status_print var) message(STATUS "${var} = ${${var}}") endmacro() # # Declare cache variable and print VARIABLE = value on configuration stage # function(declare_cache_var var default_value type docstring) set(${var} ${default_value} CACHE ${type} ${docstring}) status_print(${var}) endfunction() # # Declare cache variable which is set to TRUE if project is supposedly # loaded as root project into vscode # function(declare_cache_var_true_if_vscode var docstring) if(CMAKE_CURRENT_BINARY_DIR STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}/build") set(in_vscode TRUE) else() set(in_vscode FALSE) endif() declare_cache_var(${var} ${in_vscode} BOOL ${docstring}) endfunction() # # Create target named 'debug_print' which prints VARIABLE = value # when this target is built. Useful to debug generated expressions. #` function(debug_print var) if(NOT TARGET debug_print) add_custom_target(debug_print GLOBAL) endif() add_custom_command(COMMAND ${CMAKE_COMMAND} -E echo ${var} = ${${var}} TARGET debug_print) endfunction() # # Copy necessary dlls to target runtime directory # function(copy_dlls target) if(WIN32) add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMAND_EXPAND_LISTS) endif() endfunction() # # Select default build config with support of multi config generators # macro(set_default_build_type config_type) get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(GENERATOR_IS_MULTI_CONFIG) if(NOT DEFINED CMAKE_BUILD_TYPE) # if user passed argument '-DCMAKE_BUILD_TYPE=value', use it set(CMAKE_BUILD_TYPE ${config_type}) endif() list(FIND CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE} n) if(n LESS 0) message(FATAL_ERROR "Configuration ${CMAKE_BUILD_TYPE} is not in CMAKE_CONFIGURATION_TYPES") else() if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") set(CMAKE_DEFAULT_BUILD_TYPE ${CMAKE_BUILD_TYPE}) status_print(CMAKE_DEFAULT_BUILD_TYPE) else() message(STATUS "Default build type is not supported for generator '${CMAKE_GENERATOR}'") message(STATUS "use cmake --build . --config ${config_type}") endif() endif() else() if(CMAKE_BUILD_TYPE STREQUAL "") set(CMAKE_BUILD_TYPE ${config_type}) endif() status_print(CMAKE_BUILD_TYPE) endif() endmacro() # Copy necessary dlls to target runtime directory # function(copy_dlls target) if(WIN32) add_custom_command( TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $ COMMAND_EXPAND_LISTS) endif() endfunction() # # get property value avoiding CMake behavior - setting variable to -NOTFOUND for undefined property # function(get_target_property_if_set var target property) get_property(is_set TARGET ${target} PROPERTY ${property} SET) if(NOT is_set) unset(${var} PARENT_SCOPE) return() endif() get_property(value TARGET ${target} PROPERTY ${property}) set(${var} ${value} PARENT_SCOPE) endfunction() function(zp_source_list_requires_cxx out_var) set(_zp_uses_cxx OFF) foreach(_zp_source IN LISTS ARGN) get_filename_component(_zp_source_ext "${_zp_source}" EXT) string(TOLOWER "${_zp_source_ext}" _zp_source_ext) if(_zp_source_ext STREQUAL ".cc" OR _zp_source_ext STREQUAL ".cpp" OR _zp_source_ext STREQUAL ".cxx") set(_zp_uses_cxx ON) break() endif() endforeach() set(${out_var} "${_zp_uses_cxx}" PARENT_SCOPE) endfunction() function(zp_target_requires_cxx target out_var) if("${target}" STREQUAL "" OR NOT TARGET "${target}") set(${out_var} OFF PARENT_SCOPE) return() endif() get_target_property_if_set(_zp_sources "${target}" SOURCES) get_target_property_if_set(_zp_interface_sources "${target}" INTERFACE_SOURCES) zp_source_list_requires_cxx(_zp_requires_cxx ${_zp_sources} ${_zp_interface_sources}) set(${out_var} "${_zp_requires_cxx}" PARENT_SCOPE) endfunction() macro(zp_find_external_package package_name) get_property(_zp_imported_targets_before DIRECTORY PROPERTY IMPORTED_TARGETS) find_package(${package_name} CONFIG REQUIRED) get_property(_zp_imported_targets_after DIRECTORY PROPERTY IMPORTED_TARGETS) set(_zp_new_imported_targets ${_zp_imported_targets_after}) if(NOT "${_zp_imported_targets_before}" STREQUAL "") list(REMOVE_ITEM _zp_new_imported_targets ${_zp_imported_targets_before}) endif() foreach(_zp_target IN LISTS _zp_new_imported_targets) list(FIND ZP_EXTERNAL_IMPORTED_TARGETS "${_zp_target}" _zp_target_index) if(_zp_target_index EQUAL -1) list(APPEND ZP_EXTERNAL_IMPORTED_TARGETS "${_zp_target}") list(APPEND ZP_EXTERNAL_IMPORTED_TARGET_PACKAGES "${package_name}") endif() endforeach() endmacro() function(zp_collect_target_packages out_var) set(_zp_packages "") foreach(_zp_target IN LISTS ARGN) if("${_zp_target}" STREQUAL "" OR NOT TARGET "${_zp_target}") continue() endif() list(FIND ZP_EXTERNAL_IMPORTED_TARGETS "${_zp_target}" _zp_target_index) if(NOT _zp_target_index EQUAL -1) list(GET ZP_EXTERNAL_IMPORTED_TARGET_PACKAGES ${_zp_target_index} _zp_package_name) list(APPEND _zp_packages "${_zp_package_name}") endif() endforeach() list(REMOVE_DUPLICATES _zp_packages) set(${out_var} "${_zp_packages}" PARENT_SCOPE) endfunction() # # Unset variables if they have empty string value # macro(unset_if_empty) foreach(var ${ARGN}) if("${${var}}" STREQUAL "") unset(${var}) endif() endforeach() endmacro() # # Usage: # # include_project( TARGET # < PATH ] [QUIET] | # PACKAGE ] [QUIET] | # GIT_URL [GIT_TAG ] > # ) # # includes CMake project with one of the following ways: # add_subdirectory(project_path) or # find_package(package_name) or # FetchContent(git_url) # # If target is already defined, does nothing. If parameter QUIET is passed, does nothing # in case of failure to incude project from requested source. This allows to try to load project # from first available source, like this: # # include_project(zenohc TARGET zenohc::lib PATH ..\zenoh_c QUIET) # include_project(zenohc TARGET zenohc::lib PACKAGE zenohc QUIET) # include_project(zenohc TARGET zenohc::lib GIT_URL https://github.com/eclipse-zenoh/zenoh-c) # # QUIET parameter not supported for GIT_URL due to lack of support of such mode in FetchContent # function(include_project) __include_project(${ARGN}) # recover functions which may be replaced by included project include(${CMAKE_CURRENT_FUNCTION_LIST_FILE}) endfunction() function(__include_project project_name) include(FetchContent) include(CMakeParseArguments) cmake_parse_arguments(PARSE_ARGV 1 "ARG" "QUIET" "TARGET;PATH;PACKAGE;GIT_URL;GIT_TAG" "") unset_if_empty(ARG_PATH ARG_TARGET ARG_PACKAGE ARG_GIT_URL) if(NOT DEFINED ARG_TARGET) message(FATAL_ERROR "Non-empty TARGET parameter is required") endif() if(TARGET ${ARG_TARGET}) return() endif() if(DEFINED ARG_PATH) message(STATUS "trying to include project '${project_name} from directory '${ARG_PATH}'") file(GLOB cmakelists ${ARG_PATH}/CMakeLists.txt) if(NOT (cmakelists STREQUAL "")) message(STATUS "found cmake project in directory, including it") list(APPEND CMAKE_MESSAGE_INDENT " ") add_subdirectory(${ARG_PATH} ${project_name}) list(POP_BACK CMAKE_MESSAGE_INDENT) if(TARGET ${ARG_TARGET} OR ARG_QUIET) return() endif() message(FATAL_ERROR "Project at '${ARG_PATH}' should define target ${ARG_TARGET}") elseif(ARG_QUIET) return() else() message(FATAL_ERROR "no CMakeLists.txt file in '${ARG_PATH}'") endif() elseif(DEFINED ARG_PACKAGE) message(STATUS "trying to include project '${project_name}' from package '${ARG_PACKAGE}'") # Give priority to install directory # Useful for development when older version of the project version may be installed in system # # TODO: "if( NOT TARGET" below should be not necessary # (see https://cmake.org/cmake/help/latest/command/find_package.html, search for "override the order") # but in fact cmake fails without it when zenohc is present both in CMAKE_INSTALL_PREFIX and in /usr/local. # Consider is it still necessary after next bumping up cmake version find_package(${ARG_PACKAGE} PATHS ${CMAKE_INSTALL_PREFIX} NO_DEFAULT_PATH QUIET) if(NOT TARGET ${ARG_TARGET}) find_package(${ARG_PACKAGE} QUIET) endif() set(package_path ${${ARG_PACKAGE}_CONFIG}) if(TARGET ${ARG_TARGET}) message(STATUS "found the package on path '${package_path}'") return() endif() if(ARG_QUIET) return() endif() if("${package_path}" STREQUAL "") message(FATAL_ERROR "Package '${ARG_PACKAGE}' not found") else() message(FATAL_ERROR "Package '${ARG_PACKAGE}' on path '${package_path}' doesn't define target '${ARG_TARGET}") endif() elseif(DEFINED ARG_GIT_URL) if(DEFINED ARG_GIT_TAG) set(git_url "${ARG_GIT_URL}#{ARG_GIT_TAG}") else() set(git_url ${ARG_GIT_URL}) endif() message(STATUS "trying to include project '${project_name}' from git '${git_url}'") list(APPEND CMAKE_MESSAGE_INDENT " ") if(DEFINED ARG_GIT_TAG) FetchContent_Declare(${project_name} GIT_REPOSITORY ${ARG_GIT_URL} GIT_TAG ${ARG_GIT_TAG}) else() FetchContent_Declare(${project_name} GIT_REPOSITORY ${ARG_GIT_URL}) endif() FetchContent_MakeAvailable(${project_name}) list(POP_BACK CMAKE_MESSAGE_INDENT) if(TARGET ${ARG_TARGET}) return() endif() message(FATAL_ERROR "Project at ${git_url} should define target ${ARG_TARGET}") else() message(FATAL_ERROR "No source for project '${project_name}' specified") endif() endfunction() # # Configure set of cache variables # Include external project accordingly to these variables # # Example: # # configure_include_project(ZENOHC zenohc zenohc::lib ".." zenohc "https://github.com/eclipse-zenoh/zenoh-c" "") # # This command defines cache variables # # ZENOHC_SOURCE = "" # ZENOHC_PATH = ".." # ZENOHC_PACKAGE = "zenohc" # ZENOHC_GIT_URL = "https://github.com/eclipse-zenoh/zenoh-c" # ZENOHC_GIT_TAG = "" # # Then it tries to include the project with name 'zenohc' from first available source in order (PATH,PACKAGE,GIT_URL). # Project should define target `zenohc::lib`, otherwise cmake configuration step fails with error. # # If ZENOHC_SOURCE is set by user to value PATH, PACKAGE or GIT_URL, then the project is included from this source only. # # For example: # # cmake ../zenoh-c/examples -DZENOHC_SOURCE=GIT_URL -DZENOHC_GIT_URL=https://github.com/username/zenoh-c # # makes 'examples' project to compile with zenoh-c from username's zenoh-c git repository # function(configure_include_project var_prefix project target path package git_url git_tag) declare_cache_var( ${var_prefix}_SOURCE "" STRING "Explicit ${project} source type. Can be PATH, PACKAGE or GIT_URL. If empty, tries all these variants in order") declare_cache_var(${var_prefix}_PATH "${path}" STRING "PATH to ${project} source directory") declare_cache_var(${var_prefix}_PACKAGE "${package}" STRING "name of ${project} PACKAGE") declare_cache_var(${var_prefix}_GIT_URL "${git_url}" STRING "GIT_URL of ${project} repository") declare_cache_var(${var_prefix}_GIT_TAG "${git_tag}" STRING "GIT_TAG of ${project} repository") if(${var_prefix}_SOURCE STREQUAL "") include_project(${project} TARGET ${target} PATH ${${var_prefix}_PATH} QUIET) include_project(${project} TARGET ${target} PACKAGE ${${var_prefix}_PACKAGE} QUIET) include_project(${project} TARGET ${target} GIT_URL ${${var_prefix}_GIT_URL} GIT_TAG ${${var_prefix}_GIT_TAG}) else() include_project(${project} TARGET ${target} ${${var_prefix}_SOURCE} ${${var_prefix}_${${var_prefix}_SOURCE}} GIT_TAG ${${var_prefix}_GIT_TAG}) endif() endfunction() ================================================ FILE: cmake/platformConfig.cmake ================================================ list(PREPEND ZP_PLATFORM_DIRS "${CMAKE_CURRENT_LIST_DIR}/platforms") ================================================ FILE: cmake/platforms/arduino_esp32.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER arduino_esp32) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_ARDUINO_ESP32) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/arduino/esp32/system.c" "${PROJECT_SOURCE_DIR}/src/link/transport/bt/bt_arduino_esp32.cpp" "${PROJECT_SOURCE_DIR}/src/system/socket/esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_arduino_esp32.cpp") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_esp32.c") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/bsd.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER bsd) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_BSD) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/unix/system.c" "${PROJECT_SOURCE_DIR}/src/system/unix/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/raweth_unix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/tty_posix.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_posix.c") endif() ================================================ FILE: cmake/platforms/emscripten.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER emscripten) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_EMSCRIPTEN) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/emscripten/system.c" "${PROJECT_SOURCE_DIR}/src/system/emscripten/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/upper/ws_emscripten.c") ================================================ FILE: cmake/platforms/espidf.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER espidf) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_ESPIDF) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/espidf/system.c" "${PROJECT_SOURCE_DIR}/src/system/socket/esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_esp32.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_espidf.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_esp32.c") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/flipper.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER flipper) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_FLIPPER) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/flipper/system.c" "${PROJECT_SOURCE_DIR}/src/system/flipper/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_flipper.c") set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/freertos_lwip.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER freertos_lwip) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_FREERTOS_LWIP) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/freertos/system.c" "${PROJECT_SOURCE_DIR}/src/system/socket/lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_lwip.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip_common.c") endif() ================================================ FILE: cmake/platforms/freertos_plus_tcp.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER freertos_plus_tcp) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_FREERTOS_PLUS_TCP) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/freertos/system.c" "${PROJECT_SOURCE_DIR}/src/system/freertos/freertos_plus_tcp/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_freertos_plus_tcp.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_freertos_plus_tcp.c") ================================================ FILE: cmake/platforms/linux.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER linux) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_LINUX) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/unix/system.c" "${PROJECT_SOURCE_DIR}/src/system/unix/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/raweth_unix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/tty_posix.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_posix.c") endif() ================================================ FILE: cmake/platforms/macos.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER macos) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MACOS) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/unix/system.c" "${PROJECT_SOURCE_DIR}/src/system/unix/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/raweth_unix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/tty_posix.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_posix.c") endif() ================================================ FILE: cmake/platforms/mbed.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER mbed) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MBED) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/mbed/system.cpp" "${PROJECT_SOURCE_DIR}/src/system/mbed/network.cpp" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_mbed.cpp" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_mbed.cpp" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_mbed.cpp") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_mbed.cpp") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/opencr.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER arduino_opencr) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_ARDUINO_OPENCR) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/arduino/opencr/system.c" "${PROJECT_SOURCE_DIR}/src/system/arduino/opencr/network.cpp" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_opencr.cpp" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_opencr.cpp") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_opencr.cpp") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/posix_compatible.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER posix_compatible) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_LINUX) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/unix/system.c" "${PROJECT_SOURCE_DIR}/src/system/unix/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/raweth_unix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/tty_posix.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_posix.c") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/rpi_pico.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER rpi_pico) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_RPI_PICO) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/rpi_pico/system.c" "${PROJECT_SOURCE_DIR}/src/system/rpi_pico/usb_uart.c" "${PROJECT_SOURCE_DIR}/src/system/socket/lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_rpi_pico.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_rpi_pico.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip_common.c") endif() set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/threadx_stm32.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER threadx_stm32) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_THREADX_STM32) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/threadx/stm32/system.c" "${PROJECT_SOURCE_DIR}/src/system/threadx/stm32/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_threadx_stm32.c") set(CHECK_THREADS OFF) ================================================ FILE: cmake/platforms/windows.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER windows) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_WINDOWS _CRT_SECURE_NO_WARNINGS) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/windows/system.c" "${PROJECT_SOURCE_DIR}/src/system/windows/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_windows.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_windows.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_windows.c") endif() set(ZP_PLATFORM_LINK_LIBRARIES Ws2_32 Iphlpapi) ================================================ FILE: cmake/platforms/zephyr.cmake ================================================ set(ZP_PLATFORM_SYSTEM_LAYER zephyr) set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_ZEPHYR) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/zephyr/system.c" "${PROJECT_SOURCE_DIR}/src/system/zephyr/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_zephyr.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_zephyr.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_zephyr.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_zephyr.c") endif() ================================================ FILE: cmake/platforms.cmake ================================================ function(zp_detect_default_platform out_var) if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(${out_var} "linux" PARENT_SCOPE) elseif(POSIX_COMPATIBLE) set(${out_var} "posix_compatible" PARENT_SCOPE) elseif(CMAKE_SYSTEM_NAME MATCHES "BSD") set(${out_var} "bsd" PARENT_SCOPE) elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") set(${out_var} "macos" PARENT_SCOPE) elseif(CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(${out_var} "emscripten" PARENT_SCOPE) elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") set(${out_var} "windows" PARENT_SCOPE) elseif(CMAKE_SYSTEM_NAME MATCHES "Generic") message(FATAL_ERROR "A Generic build requires an explicit ZP_PLATFORM (for example zephyr, freertos_plus_tcp, or freertos_lwip)") elseif(CMAKE_SYSTEM_NAME MATCHES "PICO") set(${out_var} "rpi_pico" PARENT_SCOPE) else() message(FATAL_ERROR "zenoh-pico is not yet available on ${CMAKE_SYSTEM_NAME} platform") endif() endfunction() macro(zp_add_platform_dir value) list(APPEND ZP_PLATFORM_DIRS "${value}") endmacro() macro(zp_load_platform_profile profile_name) set(ZP_PLATFORM_PROFILE_FILE "") set(ZP_PLATFORM_COMPILE_DEFINITIONS "") set(ZP_PLATFORM_SOURCE_GLOBS "") set(ZP_PLATFORM_SOURCE_FILES "") set(ZP_PLATFORM_INCLUDE_DIRS "") set(ZP_PLATFORM_COMPILE_OPTIONS "") set(ZP_PLATFORM_LINK_LIBRARIES "") set(ZP_PLATFORM_SYSTEM_LAYER "") set(ZP_PLATFORM_SYSTEM_PLATFORM_HEADER "") set(CHECK_THREADS "ON") foreach(_zp_platform_dir IN LISTS ZP_PLATFORM_DIRS) if(EXISTS "${_zp_platform_dir}/${profile_name}.cmake") set(ZP_PLATFORM_PROFILE_FILE "${_zp_platform_dir}/${profile_name}.cmake") break() endif() endforeach() if(ZP_PLATFORM_PROFILE_FILE STREQUAL "") message(FATAL_ERROR "Unknown platform profile: ${profile_name}") endif() include("${ZP_PLATFORM_PROFILE_FILE}") endmacro() ================================================ FILE: colcon.pkg ================================================ { "name": "zenoh_pico", "type": "cmake", "cmake-args":[ "-DTESTS=OFF", "-DEXAMPLES=OFF", ], } ================================================ FILE: docs/.gitignore ================================================ _build ================================================ FILE: docs/api.rst ================================================ .. .. Copyright (c) 2024 ZettaScale Technology .. .. This program and the accompanying materials are made available under the .. terms of the Eclipse Public License 2.0 which is available at .. http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 .. which is available at https://www.apache.org/licenses/LICENSE-2.0. .. .. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 .. .. Contributors: .. ZettaScale Zenoh Team, .. ************* API Reference ************* Containers ============= Slice ----- Represents an array of bytes. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_slice_t .. c:type:: z_view_slice_t .. c:type:: z_loaned_slice_t .. c:type:: z_moved_slice_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_slice_empty .. autocfunction:: primitives.h::z_slice_copy_from_buf .. autocfunction:: primitives.h::z_slice_from_buf .. autocfunction:: primitives.h::z_slice_data .. autocfunction:: primitives.h::z_slice_len .. autocfunction:: primitives.h::z_slice_is_empty .. autocfunction:: primitives.h::z_view_slice_from_buf .. c:function:: void z_view_slice_empty(z_view_slice_t * slice) See :c:func:`z_slice_empty` Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_slice_drop(z_moved_slice_t * slice) .. c:function:: void z_slice_clone(z_owned_slice_t * dst, const z_loaned_slice_t * slice) .. c:function:: const z_loaned_slice_t * z_view_slice_loan(const z_view_slice_t * slice) .. c:function:: z_loaned_slice_t * z_view_slice_loan_mut(z_view_slice_t * slice) .. c:function:: const z_loaned_slice_t * z_slice_loan(const z_owned_slice_t * slice) .. c:function:: z_loaned_slice_t * z_slice_loan_mut(z_owned_slice_t * slice) .. c:function:: z_result_t z_slice_take_from_loaned(z_owned_slice_t *dst, z_loaned_slice_t *src) String ------ Represents a string without null-terminator. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_string_t .. c:type:: z_view_string_t .. c:type:: z_loaned_string_t .. c:type:: z_moved_string_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_string_empty .. autocfunction:: primitives.h::z_string_copy_from_str .. autocfunction:: primitives.h::z_string_copy_from_substr .. autocfunction:: primitives.h::z_string_from_str .. autocfunction:: primitives.h::z_string_data .. autocfunction:: primitives.h::z_string_len .. autocfunction:: primitives.h::z_string_is_empty .. autocfunction:: primitives.h::z_string_as_slice .. autocfunction:: primitives.h::z_view_string_from_str .. autocfunction:: primitives.h::z_view_string_from_substr .. c:function:: void z_view_string_empty(z_view_string_t * string) See :c:func:`z_string_empty` Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_string_drop(z_moved_string_t * string) .. c:function:: void z_string_clone(z_owned_string_t * dst, const z_loaned_string_t * string) .. c:function:: const z_loaned_string_t * z_view_string_loan(const z_view_string_t * string) .. c:function:: z_loaned_string_t * z_view_string_loan_mut(z_view_string_t * string) .. c:function:: const z_loaned_string_t * z_string_loan(const z_owned_string_t * string) .. c:function:: z_loaned_string_t * z_string_loan_mut(z_owned_string_t * string) .. c:function:: z_result_t z_string_take_from_loaned(z_owned_string_t *dst, z_loaned_string_t *src) String Array ------------ Represents an array of non null-terminated strings. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_string_array_t .. c:type:: z_loaned_string_array_t .. c:type:: z_moved_string_array_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_string_array_new .. autocfunction:: primitives.h::z_string_array_push_by_alias .. autocfunction:: primitives.h::z_string_array_push_by_copy .. autocfunction:: primitives.h::z_string_array_get .. autocfunction:: primitives.h::z_string_array_len .. autocfunction:: primitives.h::z_string_array_is_empty Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_string_array_drop(z_moved_string_array_t * string_array) .. c:function:: void z_string_array_clone(z_owned_string_array_t * dst, const z_loaned_string_array_t * string_array) .. c:function:: const z_loaned_string_array_t * z_string_array_loan(const z_owned_string_array_t * string_array) .. c:function:: z_loaned_string_array_t * z_string_array_loan_mut(z_owned_string_array_t * string_array) .. c:function:: z_result_t z_string_array_take_from_loaned(z_owned_string_array_t *dst, z_loaned_string_array_t *src) Common ====== Key expression -------------- Represents a `key expression `_ in Zenoh. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_keyexpr_t .. c:type:: z_view_keyexpr_t .. c:type:: z_loaned_keyexpr_t .. c:type:: z_moved_keyexpr_t .. autocenum:: constants.h::z_keyexpr_intersection_level_t .. autocenum:: constants.h::zp_keyexpr_canon_status_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_keyexpr_from_str .. autocfunction:: primitives.h::z_view_keyexpr_from_str .. autocfunction:: primitives.h::z_keyexpr_from_str_autocanonize .. autocfunction:: primitives.h::z_view_keyexpr_from_str_autocanonize .. autocfunction:: primitives.h::z_view_keyexpr_from_str_unchecked .. autocfunction:: primitives.h::z_keyexpr_from_substr .. autocfunction:: primitives.h::z_view_keyexpr_from_substr .. autocfunction:: primitives.h::z_keyexpr_from_substr_autocanonize .. autocfunction:: primitives.h::z_view_keyexpr_from_substr_autocanonize .. autocfunction:: primitives.h::z_view_keyexpr_from_substr_unchecked .. autocfunction:: primitives.h::z_keyexpr_as_view_string .. autocfunction:: primitives.h::z_keyexpr_canonize .. autocfunction:: primitives.h::z_keyexpr_canonize_null_terminated .. autocfunction:: primitives.h::z_keyexpr_is_canon .. autocfunction:: primitives.h::z_keyexpr_concat .. autocfunction:: primitives.h::z_keyexpr_join .. autocfunction:: primitives.h::z_keyexpr_equals .. autocfunction:: primitives.h::z_keyexpr_includes .. autocfunction:: primitives.h::z_keyexpr_intersects .. autocfunction:: primitives.h::z_keyexpr_relation_to .. autocfunction:: primitives.h::z_declare_keyexpr .. autocfunction:: primitives.h::z_undeclare_keyexpr Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_keyexpr_drop(z_moved_keyexpr_t * keyexpr) .. c:function:: void z_keyexpr_clone(z_owned_keyexpr_t * dst, const z_loaned_keyexpr_t * keyexpr) .. c:function:: const z_loaned_keyexpr_t * z_view_keyexpr_loan(const z_view_keyexpr_t * keyexpr) .. c:function:: z_loaned_keyexpr_t * z_view_keyexpr_loan_mut(z_view_keyexpr_t * keyexpr) .. c:function:: const z_loaned_keyexpr_t * z_keyexpr_loan(const z_owned_keyexpr_t * keyexpr) .. c:function:: z_loaned_keyexpr_t * z_keyexpr_loan_mut(z_owned_keyexpr_t * keyexpr) .. c:function:: z_result_t z_keyexpr_take_from_loaned(z_owned_keyexpr_t *dst, z_loaned_keyexpr_t *src) Payload ------- Types ^^^^^ see details at :ref:`owned_types_concept` .. c:type:: z_owned_bytes_t .. c:type:: z_loaned_bytes_t .. c:type:: z_moved_bytes_t .. c:type:: z_owned_bytes_writter_t .. c:type:: z_loaned_bytes_writter_t .. c:type:: z_moved_bytes_writter_t .. autoctype:: types.h::z_bytes_reader_t .. autoctype:: types.h::z_bytes_slice_iterator_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_bytes_empty .. autocfunction:: primitives.h::z_bytes_len .. autocfunction:: primitives.h::z_bytes_from_buf .. autocfunction:: primitives.h::z_bytes_from_slice .. autocfunction:: primitives.h::z_bytes_from_static_buf .. autocfunction:: primitives.h::z_bytes_from_static_str .. autocfunction:: primitives.h::z_bytes_from_str .. autocfunction:: primitives.h::z_bytes_from_string .. autocfunction:: primitives.h::z_bytes_copy_from_buf .. autocfunction:: primitives.h::z_bytes_copy_from_slice .. autocfunction:: primitives.h::z_bytes_copy_from_str .. autocfunction:: primitives.h::z_bytes_copy_from_string .. autocfunction:: primitives.h::z_bytes_to_slice .. autocfunction:: primitives.h::z_bytes_to_string .. autocfunction:: primitives.h::z_bytes_get_contiguous_view .. autocfunction:: primitives.h::z_bytes_get_slice_iterator .. autocfunction:: primitives.h::z_bytes_slice_iterator_next .. autocfunction:: primitives.h::z_bytes_get_reader .. autocfunction:: primitives.h::z_bytes_reader_read .. autocfunction:: primitives.h::z_bytes_reader_remaining .. autocfunction:: primitives.h::z_bytes_reader_seek .. autocfunction:: primitives.h::z_bytes_reader_tell .. autocfunction:: primitives.h::z_bytes_writer_append .. autocfunction:: primitives.h::z_bytes_writer_empty .. autocfunction:: primitives.h::z_bytes_writer_finish .. autocfunction:: primitives.h::z_bytes_writer_write_all Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_bytes_drop(z_moved_bytes_t * bytes) .. c:function:: void z_bytes_clone(z_owned_bytes_t * dst, const z_loaned_bytes_t * bytes) .. c:function:: const z_loaned_bytes_t * z_bytes_loan(const z_owned_bytes_t * bytes) .. c:function:: z_loaned_bytes_t * z_bytes_loan_mut(z_owned_bytes_t * bytes) .. c:function:: z_result_t z_bytes_take_from_loaned(z_owned_bytes_t *dst, z_loaned_bytes_t *src) .. c:function:: void z_bytes_writer_drop(z_moved_bytes_writer_t * bytes_writer) .. c:function:: void z_bytes_writer_clone(z_owned_bytes_writer_t * dst, const z_loaned_bytes_writer_t * bytes_writer) .. c:function:: const z_loaned_bytes_writer_t * z_bytes_writer_loan(const z_owned_bytes_writer_t * bytes_writer) .. c:function:: z_loaned_bytes_writer_t * z_bytes_writer_loan_mut(z_owned_bytes_writer_t * bytes_writer) .. c:function:: z_result_t z_bytes_writer_take_from_loaned(z_owned_bytes_writer_t *dst, z_loaned_bytes_writer_t *src) Encoding -------- Represents the encoding of a payload, in a MIME-like format. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_encoding_t .. c:type:: z_loaned_encoding_t .. c:type:: z_moved_encoding_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_encoding_from_str .. autocfunction:: primitives.h::z_encoding_from_substr .. autocfunction:: primitives.h::z_encoding_set_schema_from_str .. autocfunction:: primitives.h::z_encoding_set_schema_from_substr .. autocfunction:: primitives.h::z_encoding_to_string .. autocfunction:: primitives.h::z_encoding_equals .. autocfunction:: encoding.h::z_encoding_loan_default Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_encoding_drop(z_moved_encoding_t * encoding) .. c:function:: void z_encoding_clone(z_owned_encoding_t * dst, const z_loaned_encoding_t * encoding) .. c:function:: const z_loaned_encoding_t * z_encoding_loan(const z_owned_encoding_t * encoding) .. c:function:: z_loaned_encoding_t * z_encoding_loan_mut(z_owned_encoding_t * encoding) .. c:function:: z_result_t z_encoding_take_from_loaned(z_owned_encoding_t *dst, z_loaned_encoding_t *src) Predefined Encodings ^^^^^^^^^^^^^^^^^^^^ .. autocfunction:: encoding.h::z_encoding_zenoh_bytes .. autocfunction:: encoding.h::z_encoding_zenoh_string .. autocfunction:: encoding.h::z_encoding_zenoh_serialized .. autocfunction:: encoding.h::z_encoding_application_octet_stream .. autocfunction:: encoding.h::z_encoding_text_plain .. autocfunction:: encoding.h::z_encoding_application_json .. autocfunction:: encoding.h::z_encoding_text_json .. autocfunction:: encoding.h::z_encoding_application_cdr .. autocfunction:: encoding.h::z_encoding_application_cbor .. autocfunction:: encoding.h::z_encoding_application_yaml .. autocfunction:: encoding.h::z_encoding_text_yaml .. autocfunction:: encoding.h::z_encoding_text_json5 .. autocfunction:: encoding.h::z_encoding_application_python_serialized_object .. autocfunction:: encoding.h::z_encoding_application_protobuf .. autocfunction:: encoding.h::z_encoding_application_java_serialized_object .. autocfunction:: encoding.h::z_encoding_application_openmetrics_text .. autocfunction:: encoding.h::z_encoding_image_png .. autocfunction:: encoding.h::z_encoding_image_jpeg .. autocfunction:: encoding.h::z_encoding_image_gif .. autocfunction:: encoding.h::z_encoding_image_bmp .. autocfunction:: encoding.h::z_encoding_image_webp .. autocfunction:: encoding.h::z_encoding_application_xml .. autocfunction:: encoding.h::z_encoding_application_x_www_form_urlencoded .. autocfunction:: encoding.h::z_encoding_text_html .. autocfunction:: encoding.h::z_encoding_text_xml .. autocfunction:: encoding.h::z_encoding_text_css .. autocfunction:: encoding.h::z_encoding_text_javascript .. autocfunction:: encoding.h::z_encoding_text_markdown .. autocfunction:: encoding.h::z_encoding_text_csv .. autocfunction:: encoding.h::z_encoding_application_sql .. autocfunction:: encoding.h::z_encoding_application_coap_payload .. autocfunction:: encoding.h::z_encoding_application_json_patch_json .. autocfunction:: encoding.h::z_encoding_application_json_seq .. autocfunction:: encoding.h::z_encoding_application_jsonpath .. autocfunction:: encoding.h::z_encoding_application_jwt .. autocfunction:: encoding.h::z_encoding_application_mp4 .. autocfunction:: encoding.h::z_encoding_application_soap_xml .. autocfunction:: encoding.h::z_encoding_application_yang .. autocfunction:: encoding.h::z_encoding_audio_aac .. autocfunction:: encoding.h::z_encoding_audio_flac .. autocfunction:: encoding.h::z_encoding_audio_mp4 .. autocfunction:: encoding.h::z_encoding_audio_ogg .. autocfunction:: encoding.h::z_encoding_audio_vorbis .. autocfunction:: encoding.h::z_encoding_video_h261 .. autocfunction:: encoding.h::z_encoding_video_h263 .. autocfunction:: encoding.h::z_encoding_video_h264 .. autocfunction:: encoding.h::z_encoding_video_h265 .. autocfunction:: encoding.h::z_encoding_video_h266 .. autocfunction:: encoding.h::z_encoding_video_mp4 .. autocfunction:: encoding.h::z_encoding_video_ogg .. autocfunction:: encoding.h::z_encoding_video_raw .. autocfunction:: encoding.h::z_encoding_video_vp8 .. autocfunction:: encoding.h::z_encoding_video_vp9 Reply Error ----------- Represents a Zenoh reply error value. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_reply_err_t .. c:type:: z_loaned_reply_err_t .. c:type:: z_moved_reply_err_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_reply_err_payload .. autocfunction:: primitives.h::z_reply_err_encoding Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_reply_err_drop(z_moved_reply_err_t * reply_err) .. c:function:: void z_reply_err_clone(z_owned_reply_err_t * dst, const z_loaned_reply_err_t * reply_err) .. c:function:: const z_loaned_reply_err_t * z_reply_err_loan(const z_owned_reply_err_t * reply_err) .. c:function:: z_loaned_reply_err_t * z_reply_err_loan_mut(z_owned_reply_err_t * reply_err) .. c:function:: z_result_t z_reply_err_take_from_loaned(z_owned_reply_err_t *dst, z_loaned_reply_err_t *src) Sample ------ Represents a data sample. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_sample_t .. c:type:: z_loaned_sample_t .. c:type:: z_moved_sample_t .. autocenum:: constants.h::z_sample_kind_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_sample_timestamp .. autocfunction:: primitives.h::z_sample_attachment .. autocfunction:: primitives.h::z_sample_encoding .. autocfunction:: primitives.h::z_sample_payload .. autocfunction:: primitives.h::z_sample_keyexpr .. autocfunction:: primitives.h::z_sample_priority .. autocfunction:: primitives.h::z_sample_congestion_control .. autocfunction:: primitives.h::z_sample_express .. autocfunction:: primitives.h::z_sample_reliability .. autocfunction:: primitives.h::z_sample_kind .. autocfunction:: primitives.h::z_sample_source_info Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_sample_drop(z_moved_sample_t * sample) .. c:function:: void z_sample_clone(z_owned_sample_t * dst, const z_loaned_sample_t * sample) .. c:function:: const z_loaned_sample_t * z_sample_loan(const z_owned_sample_t * sample) .. c:function:: z_loaned_sample_t * z_sample_loan_mut(z_owned_sample_t * sample) .. c:function:: z_result_t z_sample_take_from_loaned(z_owned_sample_t *dst, z_loaned_sample_t *src) Timestamp --------- Types ^^^^^ .. c:type:: z_timestamp_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_timestamp_id .. autocfunction:: primitives.h::z_timestamp_ntp64_time Entity Global ID ---------------- Represents an entity global id. Types ^^^^^ .. c:type:: z_entity_global_id_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_entity_global_id_new .. autocfunction:: primitives.h::z_entity_global_id_eid .. autocfunction:: primitives.h::z_entity_global_id_zid Source Info ----------- Represents sample source information. Types ^^^^^ .. c:type:: z_source_info_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_source_info_new .. autocfunction:: primitives.h::z_source_info_sn .. autocfunction:: primitives.h::z_source_info_id Closures ======== A closure is a structure that contains all the elements for stateful, memory-leak-free callbacks: - context: a pointer to an arbitrary state. - call: the typical callback function. ``context`` will be passed as its last argument. - drop: allows the callback's state to be freed. ``context`` will be passed as its last argument. There is no guarantee closures won't be called concurrently. It is guaranteed that: - ``call`` will never be called once ``drop`` has started. - ``drop`` will only be called **once**, and **after every** ``call`` has ended. - The two previous guarantees imply that ``call`` and ``drop`` are never called concurrently. Sample closure --------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_sample_t .. c:type:: z_loaned_closure_sample_t .. c:type:: z_moved_closure_sample_t .. c:type:: void (* z_closure_sample_callback_t)(z_loaned_sample_t * sample, void * arg); Function pointer type for handling samples. Represents a callback function that is invoked when a sample is available for processing. Parameters: - **sample** - Pointer to a :c:type:`z_loaned_sample_t` representing the sample to be processed. - **arg** - A user-defined pointer to additional data that can be used during the processing of the sample. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_sample .. autocfunction:: primitives.h::z_closure_sample_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_sample_t * z_closure_sample_loan(const z_owned_closure_sample_t * closure) .. c:function:: void z_closure_sample_drop(z_moved_closure_sample_t * closure) Query closure ------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_query_t .. c:type:: z_loaned_closure_query_t .. c:type:: z_moved_closure_query_t .. c:type:: void (* z_closure_query_callback_t)(z_loaned_query_t * query, void * arg); Function pointer type for handling queries. Represents a callback function that is invoked when a query is available for processing. Parameters: - **query** - Pointer to a :c:type:`z_loaned_query_t` representing the query to be processed. - **arg** - A user-defined pointer to additional data that can be used during the processing of the query. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_query .. autocfunction:: primitives.h::z_closure_query_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_query_t * z_closure_query_loan(const z_owned_closure_query_t * closure) .. c:function:: void z_closure_query_drop(z_moved_closure_query_t * closure) Reply closure ------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_reply_t .. c:type:: z_loaned_closure_reply_t .. c:type:: z_moved_closure_reply_t .. c:type:: void (* z_closure_reply_callback_t)(z_loaned_reply_t * reply, void * arg); Function pointer type for handling replies. Represents a callback function that is invoked when a reply is available for processing. Parameters: - **reply** - Pointer to a :c:type:`z_loaned_reply_t` representing the reply to be processed. - **arg** - A user-defined pointer to additional data that can be used during the processing of the reply. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_reply .. autocfunction:: primitives.h::z_closure_reply_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_reply_t * z_closure_reply_loan(const z_owned_closure_reply_t * closure) .. c:function:: void z_closure_reply_drop(z_moved_closure_reply_t * closure) Hello closure ------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_hello_t .. c:type:: z_loaned_closure_hello_t .. c:type:: z_moved_closure_hello_t .. c:type:: void (* z_closure_hello_callback_t)(z_loaned_hello_t * hello, void * arg); Function pointer type for handling scouting response. Represents a callback function that is invoked when a hello is available for processing. Parameters: - **hello** - Pointer to a :c:type:`z_loaned_hello_t` representing the hello to be processed. - **arg** - A user-defined pointer to additional data that can be used during the processing of the hello. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_hello .. autocfunction:: primitives.h::z_closure_hello_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_hello_t * z_closure_hello_loan(const z_owned_closure_hello_t * closure) .. c:function:: void z_closure_hello_drop(z_moved_closure_hello_t * closure) ID closure ---------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_zid_t .. c:type:: z_loaned_closure_zid_t .. c:type:: z_moved_closure_zid_t .. c:type:: void (* z_closure_zid_callback_t)(const z_id_t * id, void * arg); Function pointer type for handling Zenoh ID routers response. Represents a callback function that is invoked when a zid is available for processing. Parameters: - **zid** - Pointer to a :c:type:`z_id_t` representing the zid to be processed. - **arg** - A user-defined pointer to additional data that can be used during the processing of the zid. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_zid .. autocfunction:: primitives.h::z_closure_zid_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_zid_t * z_closure_zid_loan(const z_owned_closure_zid_t * closure) .. c:function:: void z_closure_zid_drop(z_moved_closure_zid_t * closure) Matching closure ---------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_matching_status_t .. c:type:: z_loaned_closure_matching_status_t .. c:type:: z_moved_closure_matching_status_t .. c:type:: void (* z_closure_matching_status_callback_t)(z_matching_status_t * status, void * arg); Function pointer type for handling matching status response. Represents a callback function that is invoked when a matching status was changed. Parameters: - **status** - Pointer to a :c:type:`z_matching_status_t`. - **arg** - A user-defined pointer to additional data that can be used during the processing of the matching status. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_matching_status .. autocfunction:: primitives.h::z_closure_matching_status_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_matching_status_t * z_closure_matching_status_loan(const z_owned_closure_matching_status_t * closure) .. c:function:: void z_closure_matching_status_drop(z_moved_closure_matching_status_t * closure) Connectivity closures --------------------- .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_closure_transport_t .. c:type:: z_loaned_closure_transport_t .. c:type:: z_moved_closure_transport_t .. c:type:: z_owned_closure_link_t .. c:type:: z_loaned_closure_link_t .. c:type:: z_moved_closure_link_t .. c:type:: z_owned_closure_transport_event_t .. c:type:: z_loaned_closure_transport_event_t .. c:type:: z_moved_closure_transport_event_t .. c:type:: z_owned_closure_link_event_t .. c:type:: z_loaned_closure_link_event_t .. c:type:: z_moved_closure_link_event_t .. c:type:: void (* z_closure_transport_callback_t)(z_loaned_transport_t * transport, void * arg); .. c:type:: void (* z_closure_link_callback_t)(z_loaned_link_t * link, void * arg); .. c:type:: void (* z_closure_transport_event_callback_t)(z_loaned_transport_event_t * event, void * arg); .. c:type:: void (* z_closure_link_event_callback_t)(z_loaned_link_event_t * event, void * arg); Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_closure_transport .. autocfunction:: primitives.h::z_closure_transport_call .. autocfunction:: primitives.h::z_closure_link .. autocfunction:: primitives.h::z_closure_link_call .. autocfunction:: primitives.h::z_closure_transport_event .. autocfunction:: primitives.h::z_closure_transport_event_call .. autocfunction:: primitives.h::z_closure_link_event .. autocfunction:: primitives.h::z_closure_link_event_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_closure_transport_t * z_closure_transport_loan(const z_owned_closure_transport_t * closure) .. c:function:: void z_closure_transport_drop(z_moved_closure_transport_t * closure) .. c:function:: const z_loaned_closure_link_t * z_closure_link_loan(const z_owned_closure_link_t * closure) .. c:function:: void z_closure_link_drop(z_moved_closure_link_t * closure) .. c:function:: const z_loaned_closure_transport_event_t * z_closure_transport_event_loan(const z_owned_closure_transport_event_t * closure) .. c:function:: void z_closure_transport_event_drop(z_moved_closure_transport_event_t * closure) .. c:function:: const z_loaned_closure_link_event_t * z_closure_link_event_loan(const z_owned_closure_link_event_t * closure) .. c:function:: void z_closure_link_event_drop(z_moved_closure_link_event_t * closure) Sample miss closure ------------------- .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: ze_owned_closure_miss_t .. c:type:: ze_loaned_closure_miss_t .. c:type:: ze_moved_closure_miss_t .. c:type:: void (* ze_closure_miss_callback_t)(const ze_miss_t * miss, void *arg); Function pointer type for handling sample misses. Represents a callback function that is invoked when an advanced subscriber detects a missed sample. Parameters: - **miss** - Pointer to a :c:type:`ze_miss_t` representing the missed sample. - **arg** - A user-defined pointer to additional data that can be used during the processing of the missed sample. Functions ^^^^^^^^^ .. autocfunction:: primitives.h::ze_closure_miss .. autocfunction:: primitives.h::ze_closure_miss_call Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const ze_loaned_closure_miss_t * ze_closure_miss_loan(const ze_owned_closure_miss_t * closure) .. c:function:: void ze_closure_miss_drop(ze_moved_closure_miss_t * closure) .. _channels_concept: Channels ======== The concept of channels and handlers revolves around managing communication between different components using two types of channels: FIFO (First-In-First-Out) and Ring Buffers. These channels support handling various item types such as sample, reply, and query, with distinct methods available for each. The FIFO channel ensures that data is received in the order it was sent. It supports blocking and non-blocking (try) reception of data. If the channel is dropped, the handlers transition into a "gravestone" state, signifying that no more data will be sent or received. The Ring channel differs from FIFO in that data is overwritten if the buffer is full, but it still supports blocking and non-blocking reception of data. As with the FIFO channel, the handler can be dropped, resetting it to a gravestone state. The methods common for all channles: - `z_yyy_channel_xxx_new`: Constructs the send and receive ends of the `yyy` (`fifo` or `ring`) channel for items type `xxx`. - `z_yyy_handler_xxx_recv`: Receives an item from the channel (blocking). If no more items are available or the channel is dropped, the item transitions to the gravestone state. - `z_yyy_handler_xxx_try_recv`: Attempts to receive an item immediately (non-blocking). Returns a gravestone state if no data is available. - `z_yyy_handler_xxx_loan`: Borrows the handler for access. - `z_yyy_handler_xxx_drop`: Drops the the handler, setting it to a gravestone state. Sample channel -------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_fifo_handler_sample_t .. c:type:: z_loaned_fifo_handler_sample_t .. c:type:: z_owned_ring_handler_sample_t .. c:type:: z_loaned_ring_handler_sample_t Methods ^^^^^^^ .. c:function:: void z_fifo_channel_sample_new(z_owned_closure_sample_t * callback, z_owned_fifo_handler_sample_t * handler, size_t capacity) .. c:function:: void z_ring_channel_sample_new(z_owned_closure_sample_t * callback, z_owned_ring_handler_sample_t * handler, size_t capacity) See details at :ref:`channels_concept` .. c:function:: z_result_t z_fifo_handler_sample_recv(const z_loaned_fifo_handler_sample_t * handler, z_owned_sample_t * sample) .. c:function:: z_result_t z_fifo_handler_sample_try_recv(const z_loaned_fifo_handler_sample_t * handler, z_owned_sample_t * sample) .. c:function:: z_result_t z_ring_handler_sample_recv(const z_loaned_ring_handler_sample_t * handler, z_owned_sample_t * sample) .. c:function:: z_result_t z_ring_handler_sample_try_recv(const z_loaned_ring_handler_sample_t * handler, z_owned_sample_t * sample) See details at :ref:`channels_concept` .. c:function:: const z_loaned_fifo_handler_sample_t * z_fifo_handler_sample_loan(const z_owned_fifo_handler_sample_t * handler) .. c:function:: void z_fifo_handler_sample_drop(z_moved_fifo_handler_sample_t * handler) .. c:function:: const z_loaned_ring_handler_sample_t * z_ring_handler_sample_loan(const z_owned_ring_handler_sample_t * handler) .. c:function:: void z_ring_handler_sample_drop(z_moved_ring_handler_sample_t * handler) See details at :ref:`owned_types_concept` Query channel ------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_fifo_handler_query_t .. c:type:: z_loaned_fifo_handler_query_t .. c:type:: z_owned_ring_handler_query_t .. c:type:: z_loaned_ring_handler_query_t Methods ^^^^^^^ .. c:function:: void z_fifo_channel_query_new(z_owned_closure_query_t * callback, z_owned_fifo_handler_query_t * handler, size_t capacity) .. c:function:: void z_ring_channel_query_new(z_owned_closure_query_t * callback, z_owned_ring_handler_query_t * handler, size_t capacity) See details at :ref:`channels_concept` .. c:function:: z_result_t z_fifo_handler_query_recv(const z_loaned_fifo_handler_query_t * handler, z_owned_query_t * query) .. c:function:: z_result_t z_fifo_handler_query_try_recv(const z_loaned_fifo_handler_query_t * handler, z_owned_query_t * query) .. c:function:: z_result_t z_ring_handler_query_recv(const z_loaned_ring_handler_query_t * handler, z_owned_query_t * query) .. c:function:: z_result_t z_ring_handler_query_try_recv(const z_loaned_ring_handler_query_t * handler, z_owned_query_t * query) See details at :ref:`channels_concept` .. c:function:: const z_loaned_fifo_handler_query_t * z_fifo_handler_query_loan(const z_owned_fifo_handler_query_t * handler) .. c:function:: void z_fifo_handler_query_drop(z_moved_fifo_handler_query_t * handler) .. c:function:: const z_loaned_ring_handler_query_t * z_ring_handler_query_loan(const z_owned_ring_handler_query_t * handler) .. c:function:: void z_ring_handler_query_drop(z_moved_ring_handler_query_t * handler) See details at :ref:`owned_types_concept` Reply channel ------------- Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_fifo_handler_reply_t .. c:type:: z_loaned_fifo_handler_reply_t .. c:type:: z_owned_ring_handler_reply_t .. c:type:: z_loaned_ring_handler_reply_t Methods ^^^^^^^ .. c:function:: void z_fifo_channel_reply_new(z_owned_closure_reply_t * callback, z_owned_fifo_handler_reply_t * handler, size_t capacity) .. c:function:: void z_ring_channel_reply_new(z_owned_closure_reply_t * callback, z_owned_ring_handler_reply_t * handler, size_t capacity) See details at :ref:`channels_concept` .. c:function:: z_result_t z_fifo_handler_reply_recv(const z_loaned_fifo_handler_reply_t * handler, z_owned_reply_t * reply) .. c:function:: z_result_t z_fifo_handler_reply_try_recv(const z_loaned_fifo_handler_reply_t * handler, z_owned_reply_t * reply) .. c:function:: z_result_t z_ring_handler_reply_recv(const z_loaned_ring_handler_reply_t * handler, z_owned_reply_t * reply) .. c:function:: z_result_t z_ring_handler_reply_try_recv(const z_loaned_ring_handler_reply_t * handler, z_owned_reply_t * reply) See details at :ref:`channels_concept` .. c:function:: const z_loaned_fifo_handler_reply_t * z_fifo_handler_reply_loan(const z_owned_fifo_handler_reply_t * handler) .. c:function:: void z_fifo_handler_reply_drop(z_moved_fifo_handler_reply_t * handler) .. c:function:: const z_loaned_ring_handler_reply_t * z_ring_handler_reply_loan(const z_owned_ring_handler_reply_t * handler) .. c:function:: void z_ring_handler_reply_drop(z_moved_ring_handler_reply_t * handler) See details at :ref:`owned_types_concept` System ====== Random ------ Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_random_u8 .. autocfunction:: common/platform.h::z_random_u16 .. autocfunction:: common/platform.h::z_random_u32 .. autocfunction:: common/platform.h::z_random_u64 .. autocfunction:: common/platform.h::z_random_fill Sleep ------ Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_sleep_s .. autocfunction:: common/platform.h::z_sleep_ms .. autocfunction:: common/platform.h::z_sleep_us Time ---- Types ^^^^^ .. c:type:: z_time_t A time value that is accurate to the nearest microsecond but also has a range of years. .. c:type:: z_clock_t This is like a :c:type:`z_time_t` but has nanoseconds instead of microseconds. Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_time_now .. autocfunction:: common/platform.h::z_time_elapsed_s .. autocfunction:: common/platform.h::z_time_elapsed_ms .. autocfunction:: common/platform.h::z_time_elapsed_us .. autocfunction:: common/platform.h::z_time_now_as_str .. autocfunction:: common/platform.h::z_clock_now .. autocfunction:: common/platform.h::z_clock_elapsed_s .. autocfunction:: common/platform.h::z_clock_elapsed_ms .. autocfunction:: common/platform.h::z_clock_elapsed_us Mutex ----- Types ^^^^^ Represents a mutual exclusion (mutex) object used to ensure exclusive access to shared resources. See details at :ref:`owned_types_concept` .. c:type:: z_owned_mutex_t .. c:type:: z_loaned_mutex_t .. c:type:: z_moved_mutex_t Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_mutex_init .. autocfunction:: common/platform.h::z_mutex_lock .. autocfunction:: common/platform.h::z_mutex_unlock .. autocfunction:: common/platform.h::z_mutex_try_lock Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_mutex_drop(z_moved_mutex_t * mutex) .. c:function:: z_loaned_mutex_t * z_mutex_loan_mut(z_owned_mutex_t * mutex) Conditional Variable -------------------- Types ^^^^^ Represents a condition variable, which is a synchronization primitive that allows threads to wait until a particular condition occurs. A condition variable is used in conjunction with mutexes to enable threads to wait for signals from other threads. When a thread calls the wait function on a condition variable, it releases the associated mutex and enters a wait state until another thread signals the condition variable. See details at :ref:`owned_types_concept` .. c:type:: z_owned_condvar_t .. c:type:: z_loaned_condvar_t .. c:type:: z_moved_condvar_t Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_condvar_init .. autocfunction:: common/platform.h::z_condvar_wait .. autocfunction:: common/platform.h::z_condvar_signal Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_condvar_drop(z_moved_condvar_t * condvar) .. c:function:: const z_loaned_condvar_t * z_condvar_loan(const z_owned_condvar_t * condvar) Task ---- Types ^^^^^ Represents a task that can be executed by a thread. A task is an abstraction for encapsulating a unit of work that can be scheduled and executed by a thread. Tasks are typically used to represent asynchronous operations, allowing the program to perform multiple operations concurrently. .. c:type:: z_owned_task_t .. c:type:: z_moved_task_t Functions ^^^^^^^^^ .. autocfunction:: common/platform.h::z_task_init .. autocfunction:: common/platform.h::z_task_join .. autocfunction:: common/platform.h::z_task_detach .. autocfunction:: common/platform.h::z_task_drop Session ======= Session configuration --------------------- Represents a Zenoh configuration, used to configure Zenoh sessions upon opening. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_config_t .. c:type:: z_loaned_config_t .. c:type:: z_moved_config_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_config_default .. autocfunction:: primitives.h::zp_config_get .. autocfunction:: primitives.h::zp_config_insert Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_config_drop(z_moved_config_t * config) .. c:function:: void z_config_clone(z_owned_config_t * dst, const z_loaned_config_t * config) .. c:function:: const z_loaned_config_t * z_config_loan(const z_owned_config_t * config) .. c:function:: z_loaned_config_t * z_config_loan_mut(z_owned_config_t * config) Session management ------------------ Represents a Zenoh Session. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_session_t .. c:type:: z_loaned_session_t .. c:type:: z_moved_session_t .. c:type:: z_id_t .. autoctype:: types.h::z_open_options_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_open .. autocfunction:: primitives.h::z_open_options_default .. autocfunction:: primitives.h::z_close .. autocfunction:: primitives.h::z_session_is_closed .. autocfunction:: primitives.h::z_session_id .. autocfunction:: primitives.h::zp_spin_once .. autocfunction:: primitives.h::z_info_zid .. autocfunction:: primitives.h::z_info_routers_zid .. autocfunction:: primitives.h::z_info_peers_zid .. autocfunction:: primitives.h::z_id_to_string Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_session_t * z_session_loan(const z_owned_session_t * closure) .. c:function:: void z_session_drop(z_moved_session_t * closure) Connectivity ------------ .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. The connectivity API is available only when both ``Z_FEATURE_UNSTABLE_API`` and ``Z_FEATURE_CONNECTIVITY`` are enabled. Types ^^^^^ See details at :ref:`owned_types_concept` .. c:type:: z_owned_transport_t .. c:type:: z_loaned_transport_t .. c:type:: z_moved_transport_t .. c:type:: z_owned_link_t .. c:type:: z_loaned_link_t .. c:type:: z_moved_link_t .. c:type:: z_owned_transport_event_t .. c:type:: z_loaned_transport_event_t .. c:type:: z_moved_transport_event_t .. c:type:: z_owned_link_event_t .. c:type:: z_loaned_link_event_t .. c:type:: z_moved_link_event_t .. c:type:: z_owned_transport_events_listener_t .. c:type:: z_loaned_transport_events_listener_t .. c:type:: z_moved_transport_events_listener_t .. c:type:: z_owned_link_events_listener_t .. c:type:: z_loaned_link_events_listener_t .. c:type:: z_moved_link_events_listener_t Option Types ^^^^^^^^^^^^ .. autoctype:: types.h::z_info_links_options_t .. autoctype:: types.h::z_transport_events_listener_options_t .. autoctype:: types.h::z_link_events_listener_options_t Functions ^^^^^^^^^ .. autocfunction:: primitives.h::z_info_transports .. autocfunction:: primitives.h::z_info_links_options_default .. autocfunction:: primitives.h::z_info_links .. autocfunction:: primitives.h::z_transport_events_listener_options_default .. autocfunction:: primitives.h::z_declare_transport_events_listener .. autocfunction:: primitives.h::z_declare_background_transport_events_listener .. autocfunction:: primitives.h::z_undeclare_transport_events_listener .. autocfunction:: primitives.h::z_link_events_listener_options_default .. autocfunction:: primitives.h::z_declare_link_events_listener .. autocfunction:: primitives.h::z_declare_background_link_events_listener .. autocfunction:: primitives.h::z_undeclare_link_events_listener .. autocfunction:: primitives.h::z_transport_zid .. autocfunction:: primitives.h::z_transport_whatami .. autocfunction:: primitives.h::z_transport_is_qos .. autocfunction:: primitives.h::z_transport_is_multicast .. autocfunction:: primitives.h::z_transport_is_shm .. autocfunction:: primitives.h::z_link_zid .. autocfunction:: primitives.h::z_link_src .. autocfunction:: primitives.h::z_link_dst .. autocfunction:: primitives.h::z_link_mtu .. autocfunction:: primitives.h::z_link_is_streamed .. autocfunction:: primitives.h::z_link_is_reliable .. autocfunction:: primitives.h::z_transport_event_kind .. autocfunction:: primitives.h::z_transport_event_transport .. autocfunction:: primitives.h::z_transport_event_transport_mut .. autocfunction:: primitives.h::z_link_event_kind .. autocfunction:: primitives.h::z_link_event_link .. autocfunction:: primitives.h::z_link_event_link_mut Matching ======== Types ----- See details at :ref:`owned_types_concept` .. c:type:: z_owned_matching_listener_t .. c:type:: z_loaned_matching_listener_t .. c:type:: z_moved_matching_listener_t .. autoctype:: types.h::z_matching_status_t Functions --------- .. autocfunction:: primitives.h::z_undeclare_matching_listener Publication =========== Represents a Zenoh Publisher entity. Types ----- See details at :ref:`owned_types_concept` .. c:type:: z_owned_publisher_t .. c:type:: z_loaned_publisher_t .. c:type:: z_moved_publisher_t Option Types ------------ .. autoctype:: types.h::z_put_options_t .. autoctype:: types.h::z_delete_options_t .. autoctype:: types.h::z_publisher_options_t .. autoctype:: types.h::z_publisher_put_options_t .. autoctype:: types.h::z_publisher_delete_options_t Constants --------- .. autocenum:: constants.h::z_congestion_control_t .. autocenum:: constants.h::z_priority_t .. autocenum:: constants.h::z_reliability_t .. autocenum:: constants.h::z_locality_t Functions --------- .. autocfunction:: primitives.h::z_put .. autocfunction:: primitives.h::z_delete .. autocfunction:: primitives.h::z_declare_publisher .. autocfunction:: primitives.h::z_undeclare_publisher .. autocfunction:: primitives.h::z_publisher_put .. autocfunction:: primitives.h::z_publisher_delete .. autocfunction:: primitives.h::z_publisher_keyexpr .. autocfunction:: primitives.h::z_put_options_default .. autocfunction:: primitives.h::z_delete_options_default .. autocfunction:: primitives.h::z_publisher_options_default .. autocfunction:: primitives.h::z_publisher_put_options_default .. autocfunction:: primitives.h::z_publisher_delete_options_default .. autocfunction:: primitives.h::z_reliability_default .. autocfunction:: primitives.h::z_publisher_get_matching_status .. autocfunction:: primitives.h::z_publisher_declare_matching_listener .. autocfunction:: primitives.h::z_publisher_declare_background_matching_listener .. autocfunction:: primitives.h::z_publisher_id Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_publisher_t * z_publisher_loan(const z_owned_publisher_t * closure) .. c:function:: void z_publisher_drop(z_moved_publisher_t * closure) Advanced Publication ==================== Represents a Zenoh Advanced Publisher entity. In addition to publishing the data, it also maintains the storage, allowing matching subscribers to retrieve missed samples. Types ----- See details at :ref:`owned_types_concept` .. c:type:: ze_owned_advanced_publisher_t .. c:type:: ze_loaned_advanced_publisher_t .. c:type:: ze_moved_advanced_publisher_t Option Types ------------ .. autoctype:: advanced_publisher.h::ze_advanced_publisher_put_options_t .. autoctype:: advanced_publisher.h::ze_advanced_publisher_delete_options_t .. autoctype:: advanced_publisher.h::ze_advanced_publisher_options_t .. autoctype:: advanced_cache.h::ze_advanced_publisher_cache_options_t .. autoctype:: advanced_publisher.h::ze_advanced_publisher_sample_miss_detection_options_t Constants --------- .. autocenum:: advanced_publisher.h::ze_advanced_publisher_heartbeat_mode_t Functions --------- .. autocfunction:: advanced_publisher.h::ze_declare_advanced_publisher .. autocfunction:: advanced_publisher.h::ze_undeclare_advanced_publisher .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_put .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_delete .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_keyexpr .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_options_default .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_cache_options_default .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_sample_miss_detection_options_default .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_put_options_default .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_delete_options_default .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_get_matching_status .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_declare_matching_listener .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_declare_background_matching_listener .. autocfunction:: advanced_publisher.h::ze_advanced_publisher_id Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: const ze_loaned_advanced_publisher_t * ze_advanced_publisher_loan(const ze_owned_advanced_publisher_t * closure) .. c:function:: void ze_advanced_publisher_drop(ze_moved_advanced_publisher_t * closure) Subscription ============ Types ----- Represents a Zenoh Subscriber entity. See details at :ref:`owned_types_concept` .. c:type:: z_owned_subscriber_t .. c:type:: z_loaned_subscriber_t .. c:type:: z_moved_subscriber_t Option Types ------------ .. autoctype:: types.h::z_subscriber_options_t Functions --------- .. autocfunction:: primitives.h::z_declare_subscriber .. autocfunction:: primitives.h::z_undeclare_subscriber .. autocfunction:: primitives.h::z_declare_background_subscriber .. autocfunction:: primitives.h::z_subscriber_options_default .. autocfunction:: primitives.h::z_subscriber_keyexpr .. autocfunction:: primitives.h::z_subscriber_id Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_subscriber_t * z_subscriber_loan(const z_owned_subscriber_t * closure) .. c:function:: void z_subscriber_drop(z_moved_subscriber_t * closure) Advanced Subscriber =================== Represents a Zenoh Advanced Subscriber entity. In addition to receiving the data it is subscribed to, it is also able to receive notifications regarding missed samples and/or automatically recover them. Types ----- See details at :ref:`owned_types_concept` .. c:type:: ze_owned_advanced_subscriber_t .. c:type:: ze_loaned_advanced_subscriber_t .. c:type:: ze_moved_advanced_subscriber_t Option Types ------------ .. autoctype:: advanced_subscriber.h::ze_advanced_subscriber_history_options_t .. autoctype:: advanced_subscriber.h::ze_advanced_subscriber_recovery_options_t .. autoctype:: advanced_subscriber.h::ze_advanced_subscriber_last_sample_miss_detection_options_t .. autoctype:: advanced_subscriber.h::ze_advanced_subscriber_options_t Functions --------- .. autocfunction:: advanced_subscriber.h::ze_declare_advanced_subscriber .. autocfunction:: advanced_subscriber.h::ze_declare_background_advanced_subscriber .. autocfunction:: advanced_subscriber.h::ze_undeclare_advanced_subscriber .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_keyexpr .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_id .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_declare_sample_miss_listener .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_declare_background_sample_miss_listener .. autocfunction:: advanced_subscriber.h::ze_undeclare_sample_miss_listener .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_detect_publishers .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_detect_publishers_background .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_history_options_default .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_recovery_options_default .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_last_sample_miss_detection_options_default .. autocfunction:: advanced_subscriber.h::ze_advanced_subscriber_options_default Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_advanced_subscriber_t * z_advanced_subscriber_loan(const z_owned_advanced_subscriber_t * closure) .. c:function:: void z_advanced_subscriber_drop(z_moved_advanced_subscriber_t * closure) Queryable ========= Types ----- Represents a Zenoh Queryable entity. See details at :ref:`owned_types_concept` .. c:type:: z_owned_queryable_t .. c:type:: z_loaned_queryable_t .. c:type:: z_moved_queryable_t Represents a Zenoh Query entity, received by Zenoh queryable entities. See details at :ref:`owned_types_concept` .. c:type:: z_owned_query_t .. c:type:: z_loaned_query_t .. c:type:: z_moved_query_t Option Types ------------ .. autoctype:: types.h::z_queryable_options_t .. autoctype:: types.h::z_query_reply_options_t .. autoctype:: types.h::z_query_reply_err_options_t .. autoctype:: types.h::z_query_reply_del_options_t Functions --------- .. autocfunction:: primitives.h::z_declare_queryable .. autocfunction:: primitives.h::z_undeclare_queryable .. autocfunction:: primitives.h::z_declare_background_queryable .. autocfunction:: primitives.h::z_queryable_id .. autocfunction:: primitives.h::z_queryable_keyexpr .. autocfunction:: primitives.h::z_queryable_options_default .. autocfunction:: primitives.h::z_query_reply_options_default .. autocfunction:: primitives.h::z_query_reply_err_options_default .. autocfunction:: primitives.h::z_query_reply_del_options_default .. autocfunction:: primitives.h::z_query_keyexpr .. autocfunction:: primitives.h::z_query_parameters .. autocfunction:: primitives.h::z_query_payload .. autocfunction:: primitives.h::z_query_encoding .. autocfunction:: primitives.h::z_query_attachment .. autocfunction:: primitives.h::z_query_reply .. autocfunction:: primitives.h::z_query_reply_err .. autocfunction:: primitives.h::z_query_reply_del .. autocfunction:: primitives.h::z_query_source_info .. autocfunction:: primitives.h::z_query_accepts_replies Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: void z_queryable_drop(z_moved_queryable_t * closure) .. c:function:: const z_loaned_queryable_t * z_queryable_loan(const z_owned_queryable_t * closure) .. c:function:: void z_query_drop(z_moved_query_t * query) .. c:function:: void z_query_clone(z_owned_query_t * dst, const z_loaned_query_t * query) .. c:function:: const z_loaned_query_t * z_query_loan(const z_owned_query_t * query) .. c:function:: z_loaned_query_t * z_query_loan_mut(z_owned_query_t * query) .. c:function:: z_result_t z_query_take_from_loaned(z_owned_query_t *dst, z_loaned_query_t *src) Query ===== Types ----- Represents the reply to a query. See details at :ref:`owned_types_concept` .. c:type:: z_owned_reply_t .. c:type:: z_loaned_reply_t .. c:type:: z_moved_reply_t Option Types ------------ .. autoctype:: types.h::z_get_options_t .. autocenum:: constants.h::z_query_target_t .. autocenum:: constants.h::z_consolidation_mode_t .. autocenum:: constants.h::z_reply_keyexpr_t .. autoctype:: types.h::z_query_consolidation_t Functions --------- .. autocfunction:: primitives.h::z_get .. autocfunction:: primitives.h::z_get_with_parameters_substr .. autocfunction:: primitives.h::z_get_options_default .. autocfunction:: primitives.h::z_query_consolidation_default .. autocfunction:: primitives.h::z_query_consolidation_auto .. autocfunction:: primitives.h::z_query_consolidation_none .. autocfunction:: primitives.h::z_query_consolidation_monotonic .. autocfunction:: primitives.h::z_query_consolidation_latest .. autocfunction:: primitives.h::z_query_target_default .. autocfunction:: primitives.h::z_reply_keyexpr_default .. autocfunction:: primitives.h::z_reply_is_ok .. autocfunction:: primitives.h::z_reply_ok .. autocfunction:: primitives.h::z_reply_err Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: void z_reply_drop(z_moved_reply_t * reply) .. c:function:: void z_reply_clone(z_owned_reply_t * dst, const z_loaned_reply_t * reply) .. c:function:: const z_loaned_reply_t * z_reply_loan(const z_owned_reply_t * reply) .. c:function:: z_loaned_reply_t * z_reply_loan_mut(z_owned_reply_t * reply) .. c:function:: z_result_t z_reply_take_from_loaned(z_owned_reply_t *dst, z_loaned_reply_t *src) Querier ======= Represents a Zenoh Querier entity. Types ----- See details at :ref:`owned_types_concept` .. c:type:: z_owned_querier_t .. c:type:: z_loaned_querier_t .. c:type:: z_moved_querier_t Option Types ------------ .. autoctype:: types.h::z_querier_options_t .. autoctype:: types.h::z_querier_get_options_t Constants --------- Functions --------- .. autocfunction:: primitives.h::z_declare_querier .. autocfunction:: primitives.h::z_undeclare_querier .. autocfunction:: primitives.h::z_querier_get .. autocfunction:: primitives.h::z_querier_get_with_parameters_substr .. autocfunction:: primitives.h::z_querier_keyexpr .. autocfunction:: primitives.h::z_querier_get_matching_status .. autocfunction:: primitives.h::z_querier_declare_matching_listener .. autocfunction:: primitives.h::z_querier_declare_background_matching_listener .. autocfunction:: primitives.h::z_querier_id .. autocfunction:: primitives.h::z_querier_options_default .. autocfunction:: primitives.h::z_querier_get_options_default Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: const z_loaned_querier_t * z_querier_loan(const z_owned_querier_t * closure) .. c:function:: void z_querier_drop(z_moved_querier_t * closure) Scouting ======== Types ----- Represents the content of a `hello` message returned by a zenoh entity as a reply to a `scout` message. See details at :ref:`owned_types_concept` .. c:type:: z_owned_hello_t .. c:type:: z_loaned_hello_t .. c:type:: z_moved_hello_t Option Types ------------ .. autoctype:: types.h::z_scout_options_t Functions --------- .. autocfunction:: primitives.h::z_scout .. autocfunction:: primitives.h::z_hello_whatami .. autocfunction:: primitives.h::z_hello_locators .. autocfunction:: primitives.h::zp_hello_locators .. autocfunction:: primitives.h::z_hello_zid .. autocfunction:: primitives.h::z_whatami_to_view_string .. autocfunction:: primitives.h::z_scout_options_default Ownership Functions ------------------- See details at :ref:`owned_types_concept` .. c:function:: void z_hello_drop(z_moved_hello_t * hello) .. c:function:: void z_hello_clone(z_owned_hello_t * dst, const z_loaned_hello_t * hello) .. c:function:: const z_loaned_hello_t * z_hello_loan(const z_owned_hello_t * hello) .. c:function:: z_loaned_hello_t * z_hello_loan_mut(z_owned_hello_t * hello) .. c:function:: z_result_t z_hello_take_from_loaned(z_owned_hello_t *dst, z_loaned_hello_t *src) Serialization ======================== Types ----- Represents a data serializer (unstable). See details at :ref:`owned_types_concept` .. c:type:: ze_owned_serializer_t .. c:type:: ze_loaned_serializer_t .. c:type:: ze_moved_serializer_t .. autoctype:: serialization.h::ze_deserializer_t Functions --------- .. autocfunction:: serialization.h::ze_deserializer_from_bytes .. autocfunction:: serialization.h::ze_deserializer_deserialize_int8 .. autocfunction:: serialization.h::ze_deserializer_deserialize_int16 .. autocfunction:: serialization.h::ze_deserializer_deserialize_int32 .. autocfunction:: serialization.h::ze_deserializer_deserialize_int64 .. autocfunction:: serialization.h::ze_deserializer_deserialize_uint8 .. autocfunction:: serialization.h::ze_deserializer_deserialize_uint16 .. autocfunction:: serialization.h::ze_deserializer_deserialize_uint32 .. autocfunction:: serialization.h::ze_deserializer_deserialize_uint64 .. autocfunction:: serialization.h::ze_deserializer_deserialize_float .. autocfunction:: serialization.h::ze_deserializer_deserialize_double .. autocfunction:: serialization.h::ze_deserializer_deserialize_bool .. autocfunction:: serialization.h::ze_deserializer_deserialize_slice .. autocfunction:: serialization.h::ze_deserializer_deserialize_string .. autocfunction:: serialization.h::ze_deserializer_deserialize_sequence_length .. autocfunction:: serialization.h::ze_serializer_empty .. autocfunction:: serialization.h::ze_serializer_finish .. autocfunction:: serialization.h::ze_serializer_serialize_int8 .. autocfunction:: serialization.h::ze_serializer_serialize_int16 .. autocfunction:: serialization.h::ze_serializer_serialize_int32 .. autocfunction:: serialization.h::ze_serializer_serialize_int64 .. autocfunction:: serialization.h::ze_serializer_serialize_uint8 .. autocfunction:: serialization.h::ze_serializer_serialize_uint16 .. autocfunction:: serialization.h::ze_serializer_serialize_uint32 .. autocfunction:: serialization.h::ze_serializer_serialize_uint64 .. autocfunction:: serialization.h::ze_serializer_serialize_float .. autocfunction:: serialization.h::ze_serializer_serialize_double .. autocfunction:: serialization.h::ze_serializer_serialize_bool .. autocfunction:: serialization.h::ze_serializer_serialize_slice .. autocfunction:: serialization.h::ze_serializer_serialize_buf .. autocfunction:: serialization.h::ze_serializer_serialize_string .. autocfunction:: serialization.h::ze_serializer_serialize_str .. autocfunction:: serialization.h::ze_serializer_serialize_substr .. autocfunction:: serialization.h::ze_serializer_serialize_sequence_length .. autocfunction:: serialization.h::ze_deserialize_int8 .. autocfunction:: serialization.h::ze_deserialize_int16 .. autocfunction:: serialization.h::ze_deserialize_int32 .. autocfunction:: serialization.h::ze_deserialize_int64 .. autocfunction:: serialization.h::ze_deserialize_uint8 .. autocfunction:: serialization.h::ze_deserialize_uint16 .. autocfunction:: serialization.h::ze_deserialize_uint32 .. autocfunction:: serialization.h::ze_deserialize_uint64 .. autocfunction:: serialization.h::ze_deserialize_float .. autocfunction:: serialization.h::ze_deserialize_double .. autocfunction:: serialization.h::ze_deserialize_bool .. autocfunction:: serialization.h::ze_deserialize_slice .. autocfunction:: serialization.h::ze_deserialize_string .. autocfunction:: serialization.h::ze_deserializer_is_done .. autocfunction:: serialization.h::ze_serialize_int8 .. autocfunction:: serialization.h::ze_serialize_int16 .. autocfunction:: serialization.h::ze_serialize_int32 .. autocfunction:: serialization.h::ze_serialize_int64 .. autocfunction:: serialization.h::ze_serialize_uint8 .. autocfunction:: serialization.h::ze_serialize_uint16 .. autocfunction:: serialization.h::ze_serialize_uint32 .. autocfunction:: serialization.h::ze_serialize_uint64 .. autocfunction:: serialization.h::ze_serialize_float .. autocfunction:: serialization.h::ze_serialize_double .. autocfunction:: serialization.h::ze_serialize_bool .. autocfunction:: serialization.h::ze_serialize_slice .. autocfunction:: serialization.h::ze_serialize_buf .. autocfunction:: serialization.h::ze_serialize_string .. autocfunction:: serialization.h::ze_serialize_str .. autocfunction:: serialization.h::ze_serialize_substr Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void ze_serializer_drop(ze_moved_serializer_t * serializer) .. c:function:: void ze_serializer_clone(ze_owned_serializer_t * dst, const ze_loaned_serializer_t * serializer) .. c:function:: const ze_loaned_serializer_t * ze_serializer_loan(const ze_owned_serializer_t * serializer) .. c:function:: ze_loaned_serializer_t * ze_serializer_loan_mut(ze_owned_serializer_t * serializer) .. c:function:: z_result_t ze_serializer_take_from_loaned(ze_owned_serializer_t *dst, ze_loaned_serializer_t *src) Liveliness ======================== Types ----- .. autoctype:: liveliness.h::z_liveliness_token_options_t .. autoctype:: liveliness.h::z_liveliness_subscriber_options_t .. autoctype:: liveliness.h::z_liveliness_get_options_t Represents a Liveliness token entity. See details at :ref:`owned_types_concept` .. c:type:: z_owned_liveliness_token_t .. c:type:: z_loaned_liveliness_token_t .. c:type:: z_moved_liveliness_token_t Functions --------- .. autocfunction:: liveliness.h::z_liveliness_token_options_default .. autocfunction:: liveliness.h::z_liveliness_declare_token .. autocfunction:: liveliness.h::z_liveliness_undeclare_token .. autocfunction:: liveliness.h::z_liveliness_subscriber_options_default .. autocfunction:: liveliness.h::z_liveliness_declare_subscriber .. autocfunction:: liveliness.h::z_liveliness_declare_background_subscriber .. autocfunction:: liveliness.h::z_liveliness_get Cancellation Token ================== Types ----- Represents a Cancellation token entity, which is used to interrupt initiated queries (unstable). See details at :ref:`owned_types_concept` .. c:type:: z_owned_cancellation_token_t .. c:type:: z_loaned_cancellation_token_t .. c:type:: z_moved_cancellation_token_t Functions --------- .. autocfunction:: primitives.h::z_cancellation_token_new .. autocfunction:: primitives.h::z_cancellation_token_is_cancelled .. autocfunction:: primitives.h::z_cancellation_token_cancel Ownership Functions ^^^^^^^^^^^^^^^^^^^ See details at :ref:`owned_types_concept` .. c:function:: void z_cancellation_token_drop(z_moved_cancellation_token_t *cancellation_token) .. c:function:: void z_cancellation_token_clone(z_owned_cancellation_token_t *dst, const z_loaned_cancellation_token_t *src) .. c:function:: const z_loaned_cancellation_token_t *z_cancellation_token_loan(const z_owned_cancellation_token_t * cancellation_token) .. c:function:: z_loaned_cancellation_token * z_cancellation_token_loan_mut(z_owned_cancellation_token_t *cancellation_token) .. c:function:: z_result_t z_cancellation_token_take_from_loaned(z_owned_cancellation_token_t *dst, z_loaned_cancellation_token_t *src) Others ====== Constants --------- .. autocenum:: constants.h::z_whatami_t Macros ------ .. autocmacro:: macros.h::z_loan .. autocmacro:: macros.h::z_move .. autocmacro:: macros.h::z_clone .. autocmacro:: macros.h::z_drop .. autocmacro:: macros.h::z_closure Logging ======= .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. Zenoh-Pico provides a flexible logging system to assist with debugging and monitoring. By default, logging is disabled in release builds, but it can be enabled and configured based on the desired level of verbosity. Logging Levels -------------- Zenoh-Pico supports three logging levels: - **Error**: Only error messages are logged. This is the least verbose level. - **Info**: Logs informational messages and error messages. - **Debug**: Logs debug messages, informational messages, and error messages. This is the most verbose level. Enabling Logging ---------------- CMake build provides a variable ``ZENOH_LOG`` which accepts the following values (either uppercase or lowercase): - ``ERROR`` to enable error messages. - ``WARN`` to enable warning and higher level messages. - ``INFO`` to enable informational and higher level messages. - ``DEBUG`` to enable debug and higher level messages. - ``TRACE`` to enable trace and higher level messages. .. code-block:: bash ZENOH_LOG=debug make # build zenoh-pico with debug and higher level messages enabled When building zenoh-pico from source, logging can be enabled by defining corresponding macro, like ``-DZENOH_LOG=DEBUG``. Override Logs printing ---------------------- By default, logging use `printf`, but it can be overridden by setting `ZENOH_LOG_PRINT`: .. code-block:: bash ZENOH_LOG_PRINT=my_print make # build zenoh-pico using `my_print` instead of `printf` for logging Admin Space =========== .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. The *Admin Space* exposes internal runtime information of a Zenoh-Pico session through a queryable namespace. It allows external Zenoh applications to inspect session state such as transports, links, peers, and capabilities using standard Zenoh queries. The Admin Space is primarily intended for diagnostics, debugging, and tooling. Enabling the Admin Space ------------------------ The Admin Space is an **optional feature** and must be explicitly enabled at build time by defining the ``Z_FEATURE_ADMIN_SPACE`` configuration flag. It requires both ``Z_FEATURE_UNSTABLE_API`` and ``Z_FEATURE_QUERYABLE``. When ``Z_FEATURE_CONNECTIVITY`` and ``Z_FEATURE_PUBLICATION`` are enabled, Admin Space also publishes transport/link connectivity events and includes connectivity-specific fields in replies. When building Zenoh-Pico with CMake, this can be enabled via: .. code-block:: bash -DZ_FEATURE_ADMIN_SPACE=1 If the feature is not enabled, all Admin Space APIs will be unavailable and attempts to start the Admin Space will have no effect. Starting and Stopping the Admin Space ------------------------------------- The Admin Space is implemented as a queryable attached to a session. It can be started and stopped explicitly using the following functions: .. autocfunction:: admin_space.h::zp_start_admin_space .. autocfunction:: admin_space.h::zp_stop_admin_space Automatic Startup ----------------- The Admin Space can also be started automatically when opening a session by configuring the appropriate option in ``z_open_options_t``. When the ``auto_start_admin_space`` field is set to ``true``, the Admin Space is started immediately after the session is opened. .. code-block:: c z_open_options_t opts; z_open_options_default(&opts); opts.auto_start_admin_space = true; z_open(&session, config, &opts); ================================================ FILE: docs/concepts.rst ================================================ .. .. Copyright (c) 2024 ZettaScale Technology .. .. This program and the accompanying materials are made available under the .. terms of the Eclipse Public License 2.0 which is available at .. http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 .. which is available at https://www.apache.org/licenses/LICENSE-2.0. .. .. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 .. .. Contributors: .. ZettaScale Zenoh Team, .. ******** Concepts ******** Types Classification ==================== Zenoh-Pico types fall into these categories: - Owned types: `z_owned_xxx_t` - Loaned types: `z_loaned_xxx_t` - Moved types: `z_moved_xxx_t` - View types: `z_view_xxx_t` - Option structures: `z_xxx_options_t` - Enums and plain data structures: `z_xxx_t` .. _owned_types_concept: Owned Types `z_owned_xxx_t` --------------------------- The Zenoh-Pico library incorporates concepts like ownership, moving, and borrowing. Types prefixed with `z_owned_xxx_t` "own" external resources (e.g., memory, file descriptors). These types must be destroyed at the end of their lifecycle using the `z_xxx_drop` function or the `z_drop` macro. Example: .. code-block:: c z_owned_string_t s; z_string_copy_from_str(&s, "Hello, world!"); //... z_drop(z_move(s)); Owned objects can be passed to functions in two ways: by moving (`z_moved_xxx_t`) or loaning (`z_loaned_xxx_t`). .. _loaned_types_concept: Loaned Types `z_loaned_xxx_t` ----------------------------- To temporarily pass an owned object, it can be loaned using `z_xxx_loan` functions, which return a pointer to the corresponding `z_loaned_xxx_t`. For readability, the generic macro `z_loan` is also available. Functions accepting a loaned object can either read (`const z_loaned_xxx_t*`) or read and modify (`z_loaned_xxx_t*`) the object. In both cases, ownership remains with the caller. Example: .. code-block:: c z_owned_string_t s, s1; z_string_copy_from_str(&s, "Hello, world!"); // notice that the prototype of z_string_clone is // void z_string_clone(z_owned_string_t* dst, const z_loaned_string_t* src); // I.e. the only way to pass the source string is by loaning it z_string_clone(&s1, z_loan(s)); //... z_drop(z_move(s)); z_drop(z_move(s1)); .. _moved_types_concept: Moved types `z_moved_xxx_t` --------------------------- When a function accepts a `z_moved_xxx_t*` parameter, it takes ownership of the passed object. To pass the object, use the `z_xxx_move` function or the `z_move` macro. Once the object is moved, the caller should no longer use it. While calling `z_drop` is safe, it's not required. Note that `z_drop` itself takes ownership, so `z_move` is also needed in this case. Example: .. code-block:: c z_owned_config_t cfg; z_config_default(&cfg); z_owned_session_t session; // session takes ownership of the config if (z_open(&session, z_move(cfg)) == Z_OK) { //... z_drop(z_move(session)); } // z_drop(z_move(cfg)); // this is safe but useless .. _view_types_concept: View Types `z_view_xxx_t` ------------------------- `z_view_xxx_t` types are reference types that point to external data. These values do not need to be dropped and remain valid only as long as the data they reference is valid. Typically the view types are the variants of owned types that do not own the data. This allows to use view and owned types interchangeably. .. code-block:: c z_owned_string_t owned; z_string_copy_from_str(&owned, "Hello, world!"); z_view_string_t view; z_view_string_from_str(&view, "Hello, another world!"); z_owned_string_t dst; z_string_clone(&dst, z_loan(owned)); z_drop(z_move(dst)); z_string_clone(&dst, z_loan(view)); z_drop(z_move(dst)); z_drop(z_move(owned)); // but no need to drop view Options Structures `z_xxx_options_t` ------------------------------------ `z_xxx_options_t` are Plain Old Data (POD) structures used to pass multiple parameters to functions. This makes API compact and allows to extend the API keeping backward compatibility. Note that when an "options" structure contains `z_moved_xxx_t*` fields, assigning `z_move` to this field does not affect the owned object. However, passing the structure to a function transfers ownership of the object. Example: .. code-block:: c // assume that we want to mark our message with some metadate of type int64_t z_publisher_put_options_t options; z_publisher_put_options_default(&options); int64_t metadata = 42; z_owned_bytes_t attachment; ze_serialize_int64(&attachment, metadata); options.attachment = z_move(attachment); // the data itself is still in the `attachment` z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, "Don't panic!"); z_publisher_put(z_loan(pub), z_move(payload), &options); // the `payload` and `attachment` are consumed by the `z_publisher_put` function Other Structures and Enums `z_xxx_t` ----------------------------------------- Types named `z_xxx_t` are copyable, and can be passed by value. Some of them are just plain data structures or enums, like `z_timestamp_t`, `z_priority_t`. Some are temporary data access structures, like `z_bytes_slice_iterator_t`, `z_bytes_reader_t`, etc. .. code-block:: c z_timestamp_t ts; z_timestamp_new(&ts, z_loan(session)); z_timestamp_t ts1 = ts; Name Prefixes `z_`, `zp_` ================================ Most functions and types in the C API use the `z_` prefix, which applies to the common zenoh C API (currently Rust-based zenoh-c and pure C zenoh-pico). The `zp_` prefix is used for functions that are exclusive to zenoh-pico, zenoh-c uses the `zc_` prefix for the same purpose. ================================================ FILE: docs/conf.py ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # # Configuration file for the Sphinx documentation builder. from sys import platform from pathlib import Path from clang.cindex import Config # -- Project information ----------------------------------------------------- project = 'zenoh-pico' copyright = '2017, 2022 ZettaScale Technology Inc' author = 'ZettaScale Zenoh team' with open("../version.txt", "rt") as f: release = f.read() # -- General configuration --------------------------------------------------- master_doc = 'index' extensions = ['sphinx_c_autodoc', 'sphinx_c_autodoc.napoleon'] language = 'c' c_autodoc_roots = ['../include/zenoh-pico/api/', '../include/zenoh-pico/collections/', '../include/zenoh-pico/system/'] c_autodoc_compilation_args = [ "-DSPHINX_DOCS", "-DZ_FEATURE_AUTODOC=1", "-DZ_FEATURE_UNSTABLE_API=1", "-DZ_FEATURE_CONNECTIVITY=1", "-DZ_FEATURE_PUBLICATION=1", "-DZ_FEATURE_ADVANCED_PUBLICATION=1", "-DZ_FEATURE_ADVANCED_SUBSCRIPTION=1", "-DZ_FEATURE_SUBSCRIPTION=1", "-DZ_FEATURE_QUERY=1", "-DZ_FEATURE_QUERYABLE=1", "-DZ_FEATURE_ENCODING_VALUES=1", "-DZ_FEATURE_LIVELINESS=1", "-DZ_FEATURE_MATCHING=1", "-DZ_FEATURE_SCOUTING=1", "-DZ_FEATURE_ADMIN_SPACE=1", ] # -- Options for HTML output ------------------------------------------------- html_theme = 'sphinx_rtd_theme' breathe_debug_trace_directives = True if platform == "darwin": LIBCLANG_FILE = Path("/Library/Developer/CommandLineTools/usr/lib/libclang.dylib") LIBCLANG_CELLAR = Path("/usr/local/Cellar/llvm/14.0.6/lib/libclang.dylib") if LIBCLANG_FILE.is_file(): Config.set_library_file(LIBCLANG_FILE) elif LIBCLANG_CELLAR.is_file(): Config.set_library_file(LIBCLANG_CELLAR) else: raise ValueError(f"libclang not found. \nTried: \n {LIBCLANG_FILE}\n {LIBCLANG_CELLAR}") elif platform == "win32": raise ValueError("Windows not supported yet for building docs.") else: Config.set_library_file('/usr/lib/llvm-14/lib/libclang.so.1') # Required for readthedocs ================================================ FILE: docs/config.rst ================================================ .. .. Copyright (c) 2025 ZettaScale Technology .. .. This program and the accompanying materials are made available under the .. terms of the Eclipse Public License 2.0 which is available at .. http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 .. which is available at https://www.apache.org/licenses/LICENSE-2.0. .. .. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 .. .. Contributors: .. ZettaScale Zenoh Team, .. ************* Configuration ************* Zenoh-Pico has many run-time and compile-time configuration options. Some are passed as flags by the build system (make, cmake) while others can be modified manually. All of those options are visible in the generated `include/zenoh-pico/config.h` from a configured build or install tree. Run-time options ================ All the run-time options presented in `config.h` should be changed using the `zp_config_insert` function with the corresponding key in your application. Mode ---- Defines if a Zenoh node is a client or peer. * `Z_CONFIG_MODE_KEY`: The index of the option in the config table. * `Z_CONFIG_MODE_CLIENT`: The string value representing client mode. * `Z_CONFIG_MODE_PEER`: The string value representing peer mode. * `Z_CONFIG_MODE_DEFAULT`: The default value for client or peer mode. Connect ------- Defines one or multiple endpoints a node will connect to. * `Z_CONFIG_CONNECT_KEY`: The index of the option in the config table. * `Z_CONFIG_CONNECT_TIMEOUT_KEY`: Timeout, in milliseconds, dedicated to establishing configured connect locators. * `Z_CONFIG_CONNECT_TIMEOUT_DEFAULT`: The default timeout value for configured connect locators. * `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY`: Whether `z_open` should fail when configured connect locators cannot all be established. * `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_CLIENT_DEFAULT`: The default exit-on-failure value for client mode. * `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_PEER_DEFAULT`: The default exit-on-failure value for peer mode. .. warning:: `Z_CONFIG_CONNECT_TIMEOUT_KEY` and `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY` are currently unstable, are only available when `Z_FEATURE_UNSTABLE_API` is enabled, and may be changed in a future release. `Z_CONFIG_CONNECT_TIMEOUT_KEY` accepts the following values: * `0`: no retry, try each locator once. * `>0`: retry retryable connect failures until the timeout expires. * `-1`: retry indefinitely. The default connect timeout is `0`. `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY` accepts `true` or `false`. Its default value is `true` in client mode and `false` in peer mode. In client mode, configured connect locators are alternatives. Zenoh-Pico tries them until one succeeds or the configured timeout expires. Client mode always requires at least one connect locator to succeed before `z_open` returns successfully. Setting `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY` to `false` does not allow a client session to open without a transport. In peer mode, `z_open` also requires a primary transport before returning successfully. The primary transport is established either by opening the configured listen locator or by connecting to one configured connect locator. Once the primary transport is open, remaining connect locators are added as peers. `Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY` controls whether failures while connecting configured peer locators are tolerated. If it is `true`, non-retryable connect errors fail immediately. Retryable connect errors are retried according to `Z_CONFIG_CONNECT_TIMEOUT_KEY`; if the timeout expires before the required connectivity is established, `z_open` fails. If it is `false`, peer connections that are still failing may continue retrying from the transport task until the configured connect timeout expires. With `Z_FEATURE_MULTI_THREAD` enabled this task runs in the background; otherwise it progresses when the application spins the session tasks. Listen ------ Defines a single endpoint a node will listen on. * `Z_CONFIG_LISTEN_KEY`: The index of the option in the config table. * `Z_CONFIG_LISTEN_TIMEOUT_KEY`: Timeout, in milliseconds, dedicated to opening the configured listen locator. * `Z_CONFIG_LISTEN_TIMEOUT_DEFAULT`: The default timeout value for the configured listen locator. * `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY`: Whether `z_open` should fail when the configured listen locator cannot be opened. * `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_DEFAULT`: The default exit-on-failure value for the configured listen locator. .. warning:: `Z_CONFIG_LISTEN_TIMEOUT_KEY` and `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY` are currently unstable, are only available when `Z_FEATURE_UNSTABLE_API` is enabled, and may be changed in a future release. Zenoh-Pico supports a single configured listen locator. Configuring more than one listen locator causes `z_open` to fail. `Z_CONFIG_LISTEN_TIMEOUT_KEY` accepts the following values: * `0`: no retry, try opening the listen locator once. * `>0`: retry retryable listen failures until the timeout expires. * `-1`: retry indefinitely. The default listen timeout is `0`. `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY` accepts `true` or `false`. Its default value is `true`. If `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY` is `true`, non-retryable listen errors fail immediately. Retryable listen errors are retried according to `Z_CONFIG_LISTEN_TIMEOUT_KEY`; if the timeout expires before the listen locator is opened, `z_open` fails. In peer mode, if listening fails and `Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY` is `false`, Zenoh-Pico may still open the session by connecting to a configured connect locator. If no listen or connect locator establishes a primary transport, `z_open` fails. TLS --- With `Z_FEATURE_LINK_TLS` enabled, configure TLS using the following keys: * `Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY`: Path to the CA bundle used to verify remote certificates (required). * `Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY`: Base64-encoded CA bundle (inline alternative to the file path). * `Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_KEY`: Path to the listener private key used while listening for TLS peers (required for peers). * `Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY`: Base64-encoded listener private key. * `Z_CONFIG_TLS_LISTEN_CERTIFICATE_KEY`: Path to the listener certificate presented to clients (required for peers). * `Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY`: Base64-encoded listener certificate. * `Z_CONFIG_TLS_ENABLE_MTLS_KEY`: Set to `true`/`1`/`yes`/`on` to require client certificates (mutual TLS); defaults to disabled. * `Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_KEY`: Path to the client private key (required when mTLS is enabled). * `Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_BASE64_KEY`: Base64-encoded client private key. * `Z_CONFIG_TLS_CONNECT_CERTIFICATE_KEY`: Path to the client certificate (required when mTLS is enabled). * `Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY`: Base64-encoded client certificate. * `Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY`: Set to `false`/`0`/`no`/`off` to skip CN/SAN hostname verification; defaults to enabled. Scouting -------- Scouting is used when a node isn't provided with a connect or listen endpoint. Defines if and how the node will do the scouting. * `Z_CONFIG_MULTICAST_SCOUTING_KEY`: The index of the option in the config table. * `Z_CONFIG_MULTICAST_SCOUTING_DEFAULT`: Default value of run-time scouting activation. * `Z_CONFIG_MULTICAST_LOCATOR_KEY`: The index of the option in the config table. * `Z_CONFIG_MULTICAST_LOCATOR_DEFAULT`: Default value of endpoint use for scouting. * `Z_CONFIG_SCOUTING_TIMEOUT_KEY`: The index of the option in the config table. * `Z_CONFIG_SCOUTING_TIMEOUT_DEFAULT`: Default value for scouting timeout in milliseconds. * `Z_CONFIG_SCOUTING_WHAT_KEY`: The index of the option in the config table. * `Z_CONFIG_SCOUTING_WHAT_DEFAULT`: Default value for scouting node types as a bitmask, see :c:type:`z_whatami_t` Session id ---------- Define a custom value for the session id. Otherwise it will be random. * `Z_CONFIG_SESSION_ZID_KEY`: The index of the option in the config table. Unused ------ The following options are not currently in use, but might be in the future. * `Z_CONFIG_ADD_TIMESTAMP_KEY`: The index of the option in the config table. * `Z_CONFIG_ADD_TIMESTAMP_DEFAULT`: Default value for automatic timestamps. * `Z_CONFIG_USER_KEY`: The index of the option in the config table. * `Z_CONFIG_PASSWORD_KEY`: The index of the option in the config table. Manual compile-time options =========================== These options can be changed manually in `config.h.in` if your build system invokes zenoh-pico's own CMake. * `Z_ZID_LENGTH`: Length of session ids generated by zenoh-pico, in bytes. * `Z_PROTO_VERSION`: Id used to identify the Zenoh wire protocol version, DO NOT MODIFY. * `Z_TRANSPORT_LEASE_EXPIRE_FACTOR`: Ratio used to calculate the interval for sending keep alive messages. * `Z_JOIN_INTERVAL`: Time to wait before sending a new join message, in milliseconds, multicast transport only. * `Z_SN_RESOLUTION`: Length of the packet serial number as enum value (0: 8bits, 1: 16 bits, 2: 32 bits, 3: 64 bits) * `Z_REQ_RESOLUTION`: Length of the request id as enum value (0: 8bits, 1: 16 bits, 2: 32 bits, 3: 64 bits) * `Z_RX_CACHE_SIZE`: Width of the rx cache, when activated. * `Z_GET_TIMEOUT_DEFAULT`: Default value for a request timeout, in milliseconds. * `Z_LISTEN_MAX_CONNECTION_NB`: Maximum number of connections on a listening socket. * `ZP_ASM_NOP`: Change this options if your platform doesn't have a standard `nop` instruction. Generated compile-time options ============================== All the generated options must be changed in zenoh-pico's CMake (beware of CMake's cache) or by passing them as flags when calling zenoh-pico's CMake. * `Z_FRAG_MAX_SIZE`: Size of the defragmentation buffer, in bytes. Any packet bigger than this cannot be received by the node. * `Z_BATCH_UNICAST_SIZE`: Size of the unicast packet buffers, in bytes. Any packet bigger than this will be fragmented if possible. * `Z_BATCH_MULTICAST_SIZE`: Size of the multicast packet buffers, in bytes. Any packet bigger than this will be fragmented if possible. * `Z_CONFIG_SOCKET_TIMEOUT`: Timeout for socket options, if applicable, in milliseconds. * `Z_TRANSPORT_LEASE`: Maximum time without receiving messages from a connection before closing it, in milliseconds. * `Z_TRANSPORT_ACCEPT_TIMEOUT`: Link accept timeout in P2P mode in milliseconds (maximum amount of time the listening peer would wait to receive a response). * `Z_TRANSPORT_CONNECT_TIMEOUT`: Link connect timeout in milliseconds (maximum amount of time the connecting peer would wait to receive a response). * `Z_FEATURE_TCP_NODELAY`: (DEFAULT: ON) Toggle the `TCP_NODELAY` socket option that disables Nagle's algorithm as it can cause latency spikes. * `Z_FEATURE_AUTO_RECONNECT`: (DEFAULT: ON) Toggle the auto reconnection feature. * `Z_FEATURE_MULTICAST_DECLARATIONS`: (DEFAULT: OFF) Toggle multicast declarations. It lets nodes declare key expressions and activate write filtering but requires each node to send all the declarations every time a new node join the network. * `Z_FEATURE_RX_CACHE`: (DEFAULT: OFF) Toggle LRU cache on the Rx side, improves throughput at the cost of heap memory. * `Z_FEATURE_BATCH_TX_MUTEX`: (DEFAULT: OFF) Toggle tx mutex lock at a batch level instead of at a message level. Improves throughput at the risk of losing connection as it prevents session to send keep alive messages. * `Z_FEATURE_BATCH_PEER_MUTEX`: (DEFAULT: OFF) Toggle peer mutex lock at a batch level instead of at a message level. Prevents reception of messages from peers while batching is active, may also trigger loss of connection. The following options are here to reduce binary sizes for users that don't need those features but need the extra memory. * `Z_FEATURE_UNSTABLE_API`: (DEFAULT: OFF) Toggle compilation of unstable API functions. * `Z_FEATURE_CONNECTIVITY`: (DEFAULT: OFF) Toggle compilation of connectivity status/events API functions. This feature requires `Z_FEATURE_UNSTABLE_API`. * `Z_FEATURE_MULTI_THREAD`: (DEFAULT: ON) Toggle compilation of multi thread capabilities. Will limit the library to single thread only without this. * `Z_FEATURE_PUBLICATION`: (DEFAULT: ON) Toggle compilation of publication API functions, the library can't publish without this. * `Z_FEATURE_ADVANCED_PUBLICATION`: (DEFAULT: OFF) Toggle compilation of advanced publication API functions. * `Z_FEATURE_SUBSCRIPTION`: (DEFAULT: ON) Toggle compilation of subscription API functions, the library can't subscribe without this. * `Z_FEATURE_ADVANCED_SUBSCRIPTION`: (DEFAULT: OFF) Toggle compilation of advanced subscription API functions. * `Z_FEATURE_QUERY`: (DEFAULT: ON) Toggle compilation of query API functions, the library can't get/query without this. * `Z_FEATURE_QUERYABLE`: (DEFAULT: ON) Toggle compilation of queryable API functions, the library can't reply to queries without this. * `Z_FEATURE_SCOUTING`: (DEFAULT: ON) Toggle compilation of scouting API functions, the library can't scout without this. * `Z_FEATURE_LIVELINESS`: (DEFAULT: ON) Toggle compilation of liveliness API functions, the library can't declare liveliness tokens without this. * `Z_FEATURE_BATCHING`: (DEFAULT: ON) Toggle compilation of batching API functions, the library can't batch messages without this. * `Z_FEATURE_MATCHING`: (DEFAULT: ON) Toggle compilation of matching API functions,the library can't do matching without this. * `Z_FEATURE_INTEREST`: (DEFAULT: ON) Toggle compilation of interest protocol, the library can't do write filtering without this. * `Z_FEATURE_ENCODING_VALUES`: (DEFAULT: ON) Toggle compilation of encoding values constants, the library will not provide encoding constants without this. * `Z_FEATURE_SESSION_CHECK`: (DEFAULT: ON) Toggle session reference check. Dangerous if entities like publishers or queriers are used after a session is closed. * `Z_FEATURE_LOCAL_SUBSCRIBER`: (DEFAULT: OFF) Toggle local subscriber feature, subscribers will not be triggered by local publications without this. * `Z_FEATURE_LOCAL_QUERYABLE`: (DEFAULT: OFF) Toggle local queryable feature, queryables will not be triggered by local queries without this. * `Z_FEATURE_FRAGMENTATION`: (DEFAULT: ON) Toggle fragmentation feature, the library can't send or receive fragmented messages without this. * `Z_FEATURE_MULTICAST_TRANSPORT`: (DEFAULT: ON) Toggle multicast transport feature, the library can't handle multicast connections without this. * `Z_FEATURE_UNICAST_TRANSPORT`: (DEFAULT: ON) Toggle unicast transport feature, the library can't handle unicast connections without this. * `Z_FEATURE_RAWETH_TRANSPORT`: (DEFAULT: OFF) Toggle compilation of raw ethernet transport, the library can't handle raw ethernet connections without this. * `Z_FEATURE_UNICAST_PEER`: (DEFAULT: ON) Toggle unicast peer feature, the library can't do peer to peer unicast without this. * `Z_FEATURE_LINK_TCP`: (DEFAULT: ON) Toggle compilation of TCP link support. * `Z_FEATURE_LINK_UDP_MULTICAST`: (DEFAULT: ON) Toggle compilation of UDP multicast link support. * `Z_FEATURE_LINK_UDP_UNICAST`: (DEFAULT: ON) Toggle compilation of UDP unicast link support. * `Z_FEATURE_LINK_BLUETOOTH`: (DEFAULT: OFF) Toggle compilation of Bluetooth link support. * `Z_FEATURE_LINK_WS`: (DEFAULT: OFF) Toggle compilation of WebSocket link support. * `Z_FEATURE_LINK_SERIAL`: (DEFAULT: OFF) Toggle compilation of Serial link support. * `Z_FEATURE_LINK_SERIAL_USB`: (DEFAULT: OFF) Toggle compilation of Serial USB link support. * `Z_FEATURE_LINK_TLS`: (DEFAULT: OFF) Toggle compilation of TLS support. * `Z_FEATURE_ADMIN_SPACE`: (DEFAULT: OFF) Toggle compilation of admin space API functions. This feature requires both `Z_FEATURE_UNSTABLE_API` and `Z_FEATURE_QUERYABLE`. ================================================ FILE: docs/index.rst ================================================ .. .. Copyright (c) 2022 ZettaScale Technology .. .. This program and the accompanying materials are made available under the .. terms of the Eclipse Public License 2.0 which is available at .. http:..www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 .. which is available at https:..www.apache.org/licenses/LICENSE-2.0. .. .. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 .. .. Contributors: .. ZettaScale Zenoh Team, .. ********** zenoh-pico ********** The *libzenoh-pico* library provides a C client API for the `Zenoh protocol `_. .. toctree:: :maxdepth: 10 concepts config platforms api ================================================ FILE: docs/platforms.rst ================================================ .. .. Copyright (c) 2026 ZettaScale Technology .. .. This program and the accompanying materials are made available under the .. terms of the Eclipse Public License 2.0 which is available at .. http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 .. which is available at https://www.apache.org/licenses/LICENSE-2.0. .. .. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 .. .. Contributors: .. ZettaScale Zenoh Team, .. ********* Platforms ********* ``ZP_PLATFORM`` selects a platform profile. The profile is one CMake file. Built-in profiles are defined in ``cmake/platforms/.cmake``. Platform Selection ================== If ``ZP_PLATFORM`` is not set, Zenoh-Pico selects a default host profile. Example: .. code-block:: bash cmake -S . -B build \ -DZP_PLATFORM=linux One-File Platform Descriptors ============================= A platform profile is a CMake file. The template file is ``examples/platforms/myplatform.cmake``. A minimal profile defines: * ``ZP_PLATFORM_SOURCE_FILES``; * optional ``ZP_PLATFORM_COMPILE_DEFINITIONS``, ``ZP_PLATFORM_INCLUDE_DIRS``, ``ZP_PLATFORM_COMPILE_OPTIONS``, and ``ZP_PLATFORM_LINK_LIBRARIES``; * optional ``ZP_PLATFORM_SYSTEM_LAYER`` when the logical system-layer name differs from the profile name; * optional ``ZP_PLATFORM_SYSTEM_PLATFORM_HEADER`` when ``include/zenoh-pico/system/common/platform.h`` should include a custom header instead of a built-in one. Put all platform-specific sources into ``ZP_PLATFORM_SOURCE_FILES``: * system-layer sources; * network/socket sources; * TCP, UDP, BT, and serial transport sources; * any extra platform files. Example platform profile: .. code-block:: cmake # cmake/platforms/myrtos.cmake set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MYRTOS) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/myrtos/system.c" "${PROJECT_SOURCE_DIR}/src/system/socket/lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_myrtos.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip_common.c") endif() # Optional platform-wide additions # list(APPEND ZP_PLATFORM_SOURCE_FILES # "${PROJECT_SOURCE_DIR}/src/system/myrtos/platform_extra.c") # list(APPEND ZP_PLATFORM_INCLUDE_DIRS # "${PROJECT_SOURCE_DIR}/src/system/myrtos/include") # list(APPEND ZP_PLATFORM_COMPILE_DEFINITIONS # ZENOH_MYRTOS_BOARD) # list(APPEND ZP_PLATFORM_COMPILE_OPTIONS # -Wall) # list(APPEND ZP_PLATFORM_LINK_LIBRARIES # myrtos::sdk) Notes: * ``ZP_PLATFORM_SOURCE_FILES`` is the only source-file bucket. * The build does not classify files by role. Put a file into ``ZP_PLATFORM_SOURCE_FILES`` if it belongs to the selected platform. * Set ``ZP_PLATFORM_SYSTEM_LAYER`` only when the logical system-layer name differs from the profile name. For example, ``opencr`` uses ``arduino_opencr``. * Use ``ZP_UDP_MULTICAST_ENABLED`` when multicast sources depend on the current build configuration. Out-of-Tree Packages ==================== Out-of-tree integrations are provided as CMake packages. For each name listed in ``ZP_EXTERNAL_PACKAGES``, Zenoh-Pico runs ``find_package( CONFIG REQUIRED)``. Example: .. code-block:: bash cmake -S . -B build \ -DZP_EXTERNAL_PACKAGES=zenohpico-myrtos \ -DCMAKE_PREFIX_PATH=/path/to/prefix \ -DZP_PLATFORM=myrtos A package usually: * exports the library targets it owns; * registers the platform descriptor directory it provides. Installed package platform descriptors should use installed library targets and installed include paths instead of package source paths. Example platform descriptor from an external package: .. code-block:: cmake # /lib/cmake/zenohpico-myrtos/platforms/myrtos.cmake set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MYRTOS) set(ZP_PLATFORM_SYSTEM_PLATFORM_HEADER "zenoh_myrtos_platform.h") set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/socket/lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_lwip.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_lwip_common.c") endif() list(APPEND ZP_PLATFORM_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../../../include") list(APPEND ZP_PLATFORM_LINK_LIBRARIES myrtos::system myrtos::serial_uart) Example package config: .. code-block:: cmake include("${CMAKE_CURRENT_LIST_DIR}/zenohpicoMyrtosTargets.cmake") if(COMMAND zp_add_platform_dir) zp_add_platform_dir("${CMAKE_CURRENT_LIST_DIR}/platforms") endif() Build library targets in the package's own project, install/export them there, and load the exported targets from the package's config (``*Config.cmake``) file. Do not call ``add_library()`` or ``add_executable()`` directly in the package's config file. For a full out-of-tree example, see ``examples/packages/zenohpico-mylinux/README.md``. ================================================ FILE: docs/requirements.txt ================================================ sphinx==7.2.6 sphinx_c_autodoc==1.3.0 sphinx_rtd_theme==2.0.0 clang==14.0 ================================================ FILE: docs/zephyr/frdm_mcxn947/prj.conf ================================================ # Kernel options CONFIG_MAIN_STACK_SIZE=5120 CONFIG_HEAP_MEM_POOL_SIZE=120000 CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Generic library options CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_NANO=n CONFIG_POSIX_API=y # Generic networking options CONFIG_SERIAL=y CONFIG_NETWORKING=y CONFIG_NET_L2_ETHERNET=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_ARP=y CONFIG_NET_UDP=y CONFIG_NET_DHCPV4=n CONFIG_NET_SHELL=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_DNS_RESOLVER=y # Sockets CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POLL_MAX=4 # Network driver config CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Network buffers # Note: commented out since those are default values from Zephyr 4.1 # Please do not increase as this will quickly lead to RAM consumed due # to a need of CONFIG_NET_BUF_DATA_SIZE=1536 #CONFIG_NET_PKT_RX_COUNT=4 #CONFIG_NET_PKT_TX_COUNT=4 #CONFIG_NET_BUF_RX_COUNT=36 #CONFIG_NET_BUF_TX_COUNT=36 CONFIG_NET_CONTEXT_NET_PKT_POOL=y # Network address config CONFIG_NET_CONFIG_SETTINGS=y CONFIG_NET_CONFIG_NEED_IPV4=y CONFIG_NET_IPV4_IGMP=y CONFIG_NET_CONFIG_NEED_IPV6=y CONFIG_NET_IPV6_MLD=y CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.11.2" CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0" CONFIG_NET_CONFIG_MY_IPV4_GW="192.168.11.1" #CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.168.10.10" CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" # IP address options CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 CONFIG_NET_MAX_CONTEXTS=10 # Logging CONFIG_NET_LOG=y CONFIG_LOG=y CONFIG_NET_STATISTICS=y CONFIG_PRINTK=y # # NXP related configuration for FRDM MCX N947 and Zenoh # CONFIG_ETH_NXP_ENET_QOS_MAC=y CONFIG_ETH_NXP_ENET_QOS=y # BUF_DATA_SIZE is needed as with default value of 128 you will quickly # see error: 'Rx pkt spans over multiple DMA bufs, not implemented, drop here' # Due to BUF size it also makes problematic to increase BUF_RX/TX count above CONFIG_NET_BUF_DATA_SIZE=1536 CONFIG_ZENOH_PICO=y ================================================ FILE: docs/zephyr/nRF52840/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13.1) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(zenoh-pico-zephyr) FILE(GLOB app_sources ../src/*.c*) target_sources(app PRIVATE ${app_sources}) ================================================ FILE: docs/zephyr/nRF52840/platformio.ini ================================================ ; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:nrf52840_mdk] platform = nordicnrf52 board = nrf52840_mdk framework = zephyr ================================================ FILE: docs/zephyr/nRF52840/prj.conf ================================================ # Kernel options CONFIG_MAIN_STACK_SIZE=5120 CONFIG_HEAP_MEM_POOL_SIZE=120000 CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Generic library options CONFIG_NEWLIB_LIBC=y CONFIG_POSIX_API=y CONFIG_MAX_PTHREAD_COUNT=4 # Generic networking options CONFIG_NETWORKING=y #CONFIG_NET_L2_ETHERNET=y #CONFIG_ETH_NATIVE_POSIX=y #CONFIG_ETH_NATIVE_POSIX_RANDOM_MAC=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_ARP=y CONFIG_NET_UDP=y CONFIG_NET_DHCPV4=n CONFIG_NET_SHELL=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_DNS_RESOLVER=y # Sockets CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POSIX_NAMES=y CONFIG_NET_SOCKETS_POLL_MAX=4 # Network driver config CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Network buffers CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_BUF_RX_COUNT=80 CONFIG_NET_BUF_TX_COUNT=80 CONFIG_NET_CONTEXT_NET_PKT_POOL=y # Network address config CONFIG_NET_CONFIG_SETTINGS=y CONFIG_NET_CONFIG_NEED_IPV4=y CONFIG_NET_IPV4_IGMP=y CONFIG_NET_CONFIG_NEED_IPV6=y CONFIG_NET_IPV6_NBR_CACHE=n CONFIG_NET_IPV6_MLD=y # IP address options CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 CONFIG_NET_MAX_CONTEXTS=10 # Openthread options CONFIG_OPENTHREAD_SHELL=y CONFIG_SHELL_STACK_SIZE=3072 CONFIG_NET_L2_OPENTHREAD=y CONFIG_OPENTHREAD_DEBUG=y CONFIG_OPENTHREAD_L2_DEBUG=y CONFIG_OPENTHREAD_L2_LOG_LEVEL_INF=y CONFIG_OPENTHREAD_NETWORK_NAME="zenohnet" CONFIG_OPENTHREAD_CHANNEL=14 CONFIG_OPENTHREAD_XPANID="B0960A4EE8EEEDFD" CONFIG_OPENTHREAD_PANID=53775 # In latest zephyr, this changes to NETWORKKEY CONFIG_OPENTHREAD_MASTERKEY="EEBDB1818A18AB3B07E51CE370366B5B" CONFIG_OPENTHREAD_JOINER=y CONFIG_OPENTHREAD_JOINER_AUTOSTART=y # mbedTLS options CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=768 # Logging CONFIG_NET_LOG=y CONFIG_LOG=y CONFIG_NET_STATISTICS=y CONFIG_PRINTK=y ================================================ FILE: docs/zephyr/nucleo_f767zi/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13.1) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(zenoh-pico-zephyr) FILE(GLOB app_sources ../src/*.c*) target_sources(app PRIVATE ${app_sources}) ================================================ FILE: docs/zephyr/nucleo_f767zi/platformio.ini ================================================ ; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:nucleo_f767zi] platform = ststm32 board = nucleo_f767zi framework = zephyr ================================================ FILE: docs/zephyr/nucleo_f767zi/prj.conf ================================================ # Kernel options CONFIG_MAIN_STACK_SIZE=5120 CONFIG_HEAP_MEM_POOL_SIZE=150000 CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Generic library options CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_NANO=n CONFIG_POSIX_API=y # Generic networking options CONFIG_SERIAL=y CONFIG_NETWORKING=y CONFIG_NET_L2_ETHERNET=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_ARP=y CONFIG_NET_UDP=y CONFIG_NET_DHCPV4=n CONFIG_NET_SHELL=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_DNS_RESOLVER=y # Sockets CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POLL_MAX=4 # Network driver config CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Network buffers CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_BUF_RX_COUNT=80 CONFIG_NET_BUF_TX_COUNT=80 CONFIG_NET_CONTEXT_NET_PKT_POOL=y # Network address config CONFIG_NET_CONFIG_SETTINGS=y CONFIG_NET_CONFIG_NEED_IPV4=y CONFIG_NET_IPV4_IGMP=y CONFIG_NET_CONFIG_NEED_IPV6=y CONFIG_NET_IPV6_MLD=y CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.11.2" CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0" CONFIG_NET_CONFIG_MY_IPV4_GW="192.168.11.1" #CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.168.10.10" CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" # IP address options CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 CONFIG_NET_MAX_CONTEXTS=10 # Logging CONFIG_NET_LOG=y CONFIG_LOG=y CONFIG_NET_STATISTICS=y CONFIG_PRINTK=y ================================================ FILE: docs/zephyr/reel_board/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13.1) add_definitions(-DSHIELD=link_board_eth) #set(DTC_OVERLAY_FILE "/Users/user/.platformio/packages/framework-zephyr/boards/shields/link_board_eth/link_board_eth.overlay") set(DTC_OVERLAY_FILE $ENV{ZEPHYR_BASE}/boards/shields/link_board_eth/link_board_eth.overlay) include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) project(zenoh-pico-zephyr) FILE(GLOB app_sources ../src/*.c*) target_sources(app PRIVATE ${app_sources}) ================================================ FILE: docs/zephyr/reel_board/platformio.ini ================================================ ; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [env:reel_board] platform = nordicnrf52 board = reel_board framework = zephyr ================================================ FILE: docs/zephyr/reel_board/prj.conf ================================================ # Kernel options CONFIG_MAIN_STACK_SIZE=5120 CONFIG_HEAP_MEM_POOL_SIZE=150000 CONFIG_ENTROPY_GENERATOR=y CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Generic library options CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_NANO=n CONFIG_POSIX_API=y # Generic networking options CONFIG_NETWORKING=y CONFIG_SPI=y CONFIG_NET_L2_ETHERNET=y CONFIG_ETH_ENC424J600=y CONFIG_ETH_NATIVE_POSIX=y CONFIG_ETH_NATIVE_POSIX_RANDOM_MAC=y CONFIG_NET_IPV4=y CONFIG_NET_IPV6=y CONFIG_NET_TCP=y CONFIG_NET_ARP=y CONFIG_NET_UDP=y CONFIG_NET_DHCPV4=n CONFIG_NET_SHELL=y CONFIG_NET_MGMT=y CONFIG_NET_MGMT_EVENT=y CONFIG_DNS_RESOLVER=y # Sockets CONFIG_NET_SOCKETS=y CONFIG_NET_SOCKETS_POSIX_NAMES=y CONFIG_NET_SOCKETS_POLL_MAX=4 # Network driver config CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_INIT_STACKS=y # Network buffers CONFIG_NET_PKT_RX_COUNT=16 CONFIG_NET_PKT_TX_COUNT=16 CONFIG_NET_BUF_RX_COUNT=80 CONFIG_NET_BUF_TX_COUNT=80 CONFIG_NET_CONTEXT_NET_PKT_POOL=y # Network address config CONFIG_NET_CONFIG_SETTINGS=y CONFIG_NET_CONFIG_NEED_IPV4=y CONFIG_NET_IPV4_IGMP=y CONFIG_NET_CONFIG_NEED_IPV6=y CONFIG_NET_IPV6_MLD=y CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.11.2" CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0" CONFIG_NET_CONFIG_MY_IPV4_GW="192.168.11.1" #CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.168.10.10" CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" # IP address options CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 CONFIG_NET_MAX_CONTEXTS=10 # Logging CONFIG_NET_LOG=y CONFIG_LOG=y CONFIG_NET_STATISTICS=y CONFIG_PRINTK=y CONFIG_USE_SEGGER_RTT=y CONFIG_SHELL_BACKEND_RTT=y CONFIG_RTT_CONSOLE=y CONFIG_UART_CONSOLE=n CONFIG_LOG=y ================================================ FILE: examples/CMakeLists.txt ================================================ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) # Settings when 'examples' is the root project cmake_minimum_required(VERSION 3.20) project(zenohpico_examples LANGUAGES C) include(../cmake/helpers.cmake) set_default_build_type(Release) configure_include_project(ZENOHPICO zenohpico zenohpico::lib ".." zenohpico "https://github.com/eclipse-zenoh/zenoh-pico" "") add_custom_target(examples ALL) set(CHECK_THREADS "ON") if(CMAKE_SYSTEM_NAME MATCHES "Linux") # keep ON elseif(POSIX_COMPATIBLE) set(CHECK_THREADS "OFF") elseif(CMAKE_SYSTEM_NAME MATCHES "PICO") set(CHECK_THREADS "OFF") endif() set(THREADS_PREFER_PTHREAD_FLAG ON) if(CHECK_THREADS) find_package(Threads REQUIRED) endif() else() message(STATUS "zenoh-pico examples") add_custom_target(examples) set(THREADS_PREFER_PTHREAD_FLAG ON) if(CHECK_THREADS) find_package(Threads REQUIRED) endif() endif() function(add_example name path) add_executable(${name} ${path}) set_property(TARGET ${name} PROPERTY C_STANDARD 11) target_link_libraries(${name} PRIVATE zenohpico::lib) cmake_parse_arguments(EX "THREADS" "" "" ${ARGN}) if(EX_THREADS AND CHECK_THREADS) target_link_libraries(${name} PRIVATE Threads::Threads) endif() add_dependencies(examples ${name}) endfunction() if(UNIX) if(CMAKE_C_STANDARD MATCHES "99") add_example(z_put unix/c99/z_put.c) add_example(z_pub unix/c99/z_pub.c) add_example(z_pub_st unix/c99/z_pub_st.c) add_example(z_sub unix/c99/z_sub.c) add_example(z_sub_st unix/c99/z_sub_st.c) add_example(z_pull unix/c99/z_pull.c) add_example(z_get unix/c99/z_get.c) add_example(z_queryable unix/c99/z_queryable.c) add_example(z_info unix/c99/z_info.c) add_example(z_scout unix/c99/z_scout.c) add_example(z_ping unix/c99/z_ping.c) add_example(z_pong unix/c99/z_pong.c) else() add_example(z_put unix/c11/z_put.c) add_example(z_pub unix/c11/z_pub.c) add_example(z_pub_st unix/c11/z_pub_st.c) add_example(z_pub_attachment unix/c11/z_pub_attachment.c) add_example(z_pub_tls unix/c11/z_pub_tls.c) add_example(z_advanced_pub unix/c11/z_advanced_pub.c) add_example(z_sub unix/c11/z_sub.c) add_example(z_sub_channel unix/c11/z_sub_channel.c) add_example(z_sub_st unix/c11/z_sub_st.c) add_example(z_sub_attachment unix/c11/z_sub_attachment.c) add_example(z_sub_liveliness unix/c11/z_sub_liveliness.c) add_example(z_sub_tls unix/c11/z_sub_tls.c) add_example(z_advanced_sub unix/c11/z_advanced_sub.c) add_example(z_pull unix/c11/z_pull.c) add_example(z_get unix/c11/z_get.c) add_example(z_get_channel unix/c11/z_get_channel.c) add_example(z_get_attachment unix/c11/z_get_attachment.c) add_example(z_get_liveliness unix/c11/z_get_liveliness.c) add_example(z_querier unix/c11/z_querier.c) add_example(z_queryable unix/c11/z_queryable.c) add_example(z_queryable_channel unix/c11/z_queryable_channel.c) add_example(z_queryable_attachment unix/c11/z_queryable_attachment.c) add_example(z_info unix/c11/z_info.c) add_example(z_scout unix/c11/z_scout.c) add_example(z_ping unix/c11/z_ping.c) add_example(z_pong unix/c11/z_pong.c) add_example(z_pub_thr unix/c11/z_pub_thr.c THREADS) add_example(z_sub_thr unix/c11/z_sub_thr.c THREADS) add_example(z_bytes unix/c11/z_bytes.c) add_example(z_liveliness unix/c11/z_liveliness.c) add_example(z_get_lat unix/c11/z_get_lat.c) add_example(z_queryable_lat unix/c11/z_queryable_lat.c THREADS) endif() elseif(MSVC) add_example(z_put windows/z_put.c) add_example(z_pub windows/z_pub.c) add_example(z_pub_st windows/z_pub_st.c) add_example(z_sub windows/z_sub.c) add_example(z_sub_st windows/z_sub_st.c) add_example(z_pull windows/z_pull.c) add_example(z_get windows/z_get.c) add_example(z_queryable windows/z_queryable.c) add_example(z_info windows/z_info.c) add_example(z_scout windows/z_scout.c) add_example(z_ping windows/z_ping.c) add_example(z_pong windows/z_pong.c) endif() ================================================ FILE: examples/arduino/z_get.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_QUERY == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" // Client mode values (comment/uncomment as needed) #define MODE "client" #define LOCATOR "" // If empty, it will scout // Peer mode values (comment/uncomment as needed) // #define MODE "peer" // #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #define KEYEXPR "demo/example/**" #define VALUE "" z_owned_session_t s; void reply_dropper(void *ctx) { (void)(ctx); Serial.println(" >> Received query final notification"); } void reply_handler(z_loaned_reply_t *oreply, void *ctx) { (void)(ctx); if (z_reply_is_ok(oreply)) { const z_loaned_sample_t *sample = z_reply_ok(oreply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); Serial.print(" >> [Get listener] Received ("); Serial.write(z_string_data(z_view_string_loan(&keystr)), z_string_len(z_view_string_loan(&keystr))); Serial.print(", "); Serial.write(z_string_data(z_string_loan(&replystr)), z_string_len(z_string_loan(&replystr))); Serial.println(")"); z_string_drop(z_string_move(&replystr)); } else { Serial.println(" >> Received an error"); } } void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session Serial.print("Opening Zenoh Session..."); if (z_open(&s, z_config_move(&config), NULL) < 0) { Serial.println("Unable to open session!"); while (1) { ; } } Serial.println("OK"); Serial.println("Zenoh setup finished!"); delay(300); } void loop() { delay(5000); Serial.print("Sending Query "); Serial.print(KEYEXPR); Serial.println(" ..."); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_bytes_move(&payload); } z_owned_closure_reply_t callback; z_closure_reply(&callback, reply_handler, reply_dropper, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_get(z_session_loan(&s), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&callback), &opts) < 0) { Serial.println("Unable to send query."); } } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/arduino/z_pub.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_PUBLICATION == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" // Client mode values (comment/uncomment as needed) #define MODE "client" #define LOCATOR "" // If empty, it will scout // Peer mode values (comment/uncomment as needed) // #define MODE "peer" // #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[ARDUINO]{ESP32} Publication from Zenoh-Pico!" z_owned_session_t s; z_owned_publisher_t pub; static int idx = 0; void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session Serial.print("Opening Zenoh Session..."); if (z_open(&s, z_config_move(&config), NULL) < 0) { Serial.println("Unable to open session!"); while (1) { ; } } Serial.println("OK"); // Declare Zenoh publisher Serial.print("Declaring publisher for "); Serial.print(KEYEXPR); Serial.println("..."); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_session_loan(&s), &pub, z_view_keyexpr_loan(&ke), NULL) < 0) { Serial.println("Unable to declare publisher for key expression!"); while (1) { ; } } Serial.println("OK"); Serial.println("Zenoh setup finished!"); delay(300); } void loop() { delay(1000); char buf[256]; sprintf(buf, "[%4d] %s", idx++, VALUE); Serial.print("Writing Data ('"); Serial.print(KEYEXPR); Serial.print("': '"); Serial.print(buf); Serial.println("')"); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); if (z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL) < 0) { Serial.println("Error while publishing data"); } } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/arduino/z_pull.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" // Client mode values (comment/uncomment as needed) #define MODE "client" #define LOCATOR "" // If empty, it will scout // Peer mode values (comment/uncomment as needed) // #define MODE "peer" // #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; z_owned_session_t s; z_owned_subscriber_t sub; z_owned_ring_handler_sample_t handler; void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session Serial.print("Opening Zenoh Session..."); if (z_open(&s, z_config_move(&config), NULL) < 0) { Serial.println("Unable to open session!"); while (1) { ; } } Serial.println("OK"); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_ring_channel_sample_new(&closure, &handler, SIZE); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) < 0) { Serial.println("Unable to declare subscriber."); return; } Serial.println("OK"); Serial.println("Zenoh setup finished!"); delay(300); } void loop() { z_owned_sample_t sample; z_result_t res; for (res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample); res == Z_OK; res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_sample_loan(&sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_sample_loan(&sample)), &value); Serial.print(">> [Subscriber] Pulled ("); Serial.write(z_string_data(z_view_string_loan(&keystr)), z_string_len(z_view_string_loan(&keystr))); Serial.print(": "); Serial.write(z_string_data(z_string_loan(&value)), z_string_len(z_string_loan(&value))); Serial.println(")"); z_string_drop(z_string_move(&value)); z_sample_drop(z_sample_move(&sample)); } if (res == Z_CHANNEL_NODATA) { delay(INTERVAL); } } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/arduino/z_queryable.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_QUERYABLE == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" // Client mode values (comment/uncomment as needed) #define MODE "client" #define LOCATOR "" // If empty, it will scout // Peer mode values (comment/uncomment as needed) // #define MODE "peer" // #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[ARDUINO]{ESP32} Queryable from Zenoh-Pico!" z_owned_session_t s; z_owned_queryable_t qable; void query_handler(z_loaned_query_t *query, void *arg) { z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); Serial.print(" >> [Queryable handler] Received Query '"); Serial.write(z_string_data(z_view_string_loan(&keystr)), z_string_len(z_view_string_loan(&keystr))); Serial.println("'"); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_string_loan(&payload_string)) > 1) { Serial.print(" with value '"); Serial.write(z_string_data(z_string_loan(&payload_string)), z_string_len(z_string_loan(&payload_string))); Serial.println("'"); } z_string_drop(z_string_move(&payload_string)); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_view_keyexpr_loan(&ke), z_bytes_move(&reply_payload), NULL); } void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session Serial.print("Opening Zenoh Session..."); if (z_open(&s, z_config_move(&config), NULL) < 0) { Serial.println("Unable to open session!"); while (1) { ; } } Serial.println("OK"); // Declare Zenoh queryable Serial.print("Declaring Queryable on "); Serial.print(KEYEXPR); Serial.println(" ..."); z_owned_closure_query_t callback; z_closure_query(&callback, query_handler, NULL, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_queryable(z_session_loan(&s), &qable, z_view_keyexpr_loan(&ke), z_closure_query_move(&callback), NULL) < 0) { Serial.println("Unable to declare queryable."); while (1) { ; } } Serial.println("OK"); Serial.println("Zenoh setup finished!"); delay(300); } void loop() { delay(1000); } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/arduino/z_scout.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_SCOUTING == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" uint8_t zid_len(z_id_t id) { uint8_t len = 16; while (len > 0) { --len; if (id.id[len] != 0) { ++len; break; } } return len; } void fprintzid(z_id_t zid) { unsigned int zidlen = zid_len(zid); if (zidlen == 0) { Serial.print("None"); } else { Serial.print("Some("); for (unsigned int i = 0; i < zidlen; i++) { Serial.print(zid.id[i], HEX); } Serial.print(")"); } } void fprintwhatami(z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); Serial.write(z_string_data(z_view_string_loan(&s)), z_string_len(z_view_string_loan(&s))); } void fprintlocators(const z_loaned_string_array_t *locs) { Serial.print("["); size_t len = z_string_array_len(locs); for (unsigned int i = 0; i < len; i++) { Serial.print("'"); const z_loaned_string_t *str = z_string_array_get(locs, i); Serial.write(z_string_data(str), z_string_len(str)); Serial.print("'"); if (i < len - 1) { Serial.print(", "); } } Serial.print("]"); } void fprinthello(const z_loaned_hello_t *hello) { Serial.print(" >> Hello { zid: "); fprintzid(z_hello_zid(hello)); Serial.print(", whatami: "); fprintwhatami(z_hello_whatami(hello)); Serial.print(", locators: "); fprintlocators(zp_hello_locators(hello)); Serial.println(" }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(hello); Serial.println(""); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); } void loop() { int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure_hello(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_config_move(&config), z_closure_hello_move(&closure), NULL); } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/arduino/z_sub.ino ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 // WiFi-specific parameters #define SSID "SSID" #define PASS "PASS" // Client mode values (comment/uncomment as needed) #define MODE "client" #define LOCATOR "" // If empty, it will scout // Peer mode values (comment/uncomment as needed) // #define MODE "peer" // #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #define KEYEXPR "demo/example/**" z_owned_session_t s; z_owned_subscriber_t sub; void data_handler(z_loaned_sample_t *sample, void *arg) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); Serial.print(" >> [Subscription listener] Received ("); Serial.write(z_string_data(z_view_string_loan(&keystr)), z_string_len(z_view_string_loan(&keystr))); Serial.print(", "); Serial.write(z_string_data(z_string_loan(&value)), z_string_len(z_string_loan(&value))); Serial.println(")"); z_string_drop(z_string_move(&value)); } void setup() { // Initialize Serial for debug Serial.begin(115200); while (!Serial) { delay(1000); } // Set WiFi in STA mode and trigger attachment Serial.print("Connecting to WiFi..."); WiFi.mode(WIFI_STA); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(1000); } Serial.println("OK"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session Serial.print("Opening Zenoh Session..."); if (z_open(&s, z_config_move(&config), NULL) < 0) { Serial.println("Unable to open session!"); while (1) { ; } } Serial.println("OK"); // Declare Zenoh subscriber Serial.print("Declaring Subscriber on "); Serial.print(KEYEXPR); Serial.println(" ..."); z_owned_closure_sample_t callback; z_closure_sample(&callback, data_handler, NULL, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&callback), NULL) < 0) { Serial.println("Unable to declare subscriber."); while (1) { ; } } Serial.println("OK"); Serial.println("Zenoh setup finished!"); delay(300); } void loop() { delay(1000); } #else void setup() { Serial.println("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it."); return; } void loop() {} #endif ================================================ FILE: examples/espidf/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #if Z_FEATURE_QUERY == 1 #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" #define VALUE "" static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void reply_dropper(void *ctx) { printf(" >> Received query final notification\n"); } void reply_handler(z_loaned_reply_t *oreply, void *ctx) { if (z_reply_is_ok(oreply)) { const z_loaned_sample_t *sample = z_reply_ok(oreply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(" >> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(" >> Received an error\n"); } } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); while (1) { sleep(5); printf("Sending Query '%s'...\n", KEYEXPR); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_move(payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); exit(-1); } } printf("Closing Zenoh Session..."); z_drop(z_move(s)); printf("OK!\n"); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it.\n"); } #endif ================================================ FILE: examples/espidf/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[ESPIDF]{ESP32} Publication from Zenoh-Pico!" static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring publisher for '%s'...", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); exit(-1); } printf("OK\n"); char buf[256]; for (int idx = 0; 1; ++idx) { sleep(1); sprintf(buf, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); } printf("Closing Zenoh Session..."); z_drop(z_move(pub)); z_drop(z_move(s)); printf("OK!\n"); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/espidf/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, SIZE); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); exit(-1); } printf("Pulling data every %zu ms... Ring size: %zd\n", INTERVAL, SIZE); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", INTERVAL); z_sleep_ms(INTERVAL); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); printf("OK!\n"); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); } #endif ================================================ FILE: examples/espidf/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 #if Z_FEATURE_QUERYABLE == 1 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[ESPIDF]{ESP32} Queryable from Zenoh-Pico!" static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_loan(ke), z_move(reply_payload), NULL); } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); // Declare Zenoh queryable printf("Declaring Queryable on %s...", KEYEXPR); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare queryable.\n"); exit(-1); } printf("OK\n"); printf("Zenoh setup finished!\n"); while (1) { sleep(1); } printf("Closing Zenoh Session..."); z_drop(z_move(qable)); z_drop(z_move(s)); printf("OK!\n"); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); } #endif ================================================ FILE: examples/espidf/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #if Z_FEATURE_SCOUTING == 1 #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure_hello(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_config_move(&config), z_closure_hello_move(&closure), NULL); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); } #endif ================================================ FILE: examples/espidf/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define ESP_WIFI_SSID "SSID" #define ESP_WIFI_PASS "PASS" #define ESP_MAXIMUM_RETRY 5 #define WIFI_CONNECTED_BIT BIT0 static bool s_is_wifi_connected = false; static EventGroupHandle_t s_event_group_handler; static int s_retry_count = 0; #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count < ESP_MAXIMUM_RETRY) { esp_wifi_connect(); s_retry_count++; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { xEventGroupSetBits(s_event_group_handler, WIFI_CONNECTED_BIT); s_retry_count = 0; } } void wifi_init_sta(void) { s_event_group_handler = xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&config)); esp_event_handler_instance_t handler_any_id; esp_event_handler_instance_t handler_got_ip; ESP_ERROR_CHECK( esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &handler_any_id)); ESP_ERROR_CHECK( esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &handler_got_ip)); wifi_config_t wifi_config = {.sta = { .ssid = ESP_WIFI_SSID, .password = ESP_WIFI_PASS, }}; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits = xEventGroupWaitBits(s_event_group_handler, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { s_is_wifi_connected = true; } ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_got_ip)); ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, handler_any_id)); vEventGroupDelete(s_event_group_handler); } void data_handler(z_loaned_sample_t* sample, void* arg) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(" >> [Subscriber handler] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); } void app_main() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // Set WiFi in STA mode and trigger attachment printf("Connecting to WiFi..."); wifi_init_sta(); while (!s_is_wifi_connected) { printf("."); sleep(1); } printf("OK!\n"); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring Subscriber on '%s'...", KEYEXPR); z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); exit(-1); } printf("OK!\n"); while (1) { sleep(1); } printf("Closing Zenoh Session..."); z_drop(z_move(sub)); z_drop(z_move(s)); printf("OK!\n"); } #else void app_main() { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/CMakeLists.txt ================================================ # # Copyright (c) 2023 Fictionlab sp. z o.o. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # Błażej Sowa, cmake_minimum_required(VERSION 3.20) project(zenohpico_freertos_plus_tcp_examples) set(CMAKE_SYSTEM_NAME "Generic") set(CMAKE_C_STANDARD 11) include(../../cmake/helpers.cmake) set_default_build_type(Release) find_package(PkgConfig REQUIRED) pkg_check_modules(SLIRP REQUIRED slirp) add_library(slirp INTERFACE) target_link_libraries(slirp INTERFACE ${SLIRP_LINK_LIBRARIES}) target_include_directories(slirp INTERFACE ${SLIRP_INCLUDE_DIRS}) target_compile_options(slirp INTERFACE -Wno-error) include(FetchContent) FetchContent_Declare(freertos_kernel GIT_REPOSITORY "https://github.com/FreeRTOS/FreeRTOS-Kernel.git" GIT_TAG "V10.6.1" ) FetchContent_Declare(freertos_plus_tcp GIT_REPOSITORY "https://github.com/FreeRTOS/FreeRTOS-Plus-TCP.git" GIT_TAG "34148c3" GIT_SUBMODULES "" ) add_library(freertos_config INTERFACE) target_include_directories(freertos_config SYSTEM INTERFACE include) target_compile_options(freertos_config INTERFACE -Wno-error) set(FREERTOS_HEAP "3" CACHE STRING "" FORCE) set(FREERTOS_PORT "GCC_POSIX" CACHE STRING "" FORCE) set(FREERTOS_PLUS_TCP_NETWORK_IF "LIBSLIRP" CACHE STRING "" FORCE) set(FREERTOS_PLUS_TCP_BUFFER_ALLOCATION "2" CACHE STRING "" FORCE) FetchContent_MakeAvailable(freertos_kernel freertos_plus_tcp) set(BUILD_SHARED_LIBS OFF) set(ZP_PLATFORM "freertos_plus_tcp" CACHE STRING "Zenoh-Pico platform profile") set(ZENOH_LOG DEBUG) configure_include_project(ZENOHPICO zenohpico zenohpico::lib "../.." zenohpico "https://github.com/eclipse-zenoh/zenoh-pico" "") target_link_libraries(zenohpico_static PRIVATE freertos_kernel freertos_plus_tcp ) add_library(main OBJECT main.c) target_link_libraries(main freertos_kernel freertos_plus_tcp ) function(add_example name) add_executable(${name} ${name}.c) target_link_libraries(${name} main freertos_kernel freertos_plus_tcp zenohpico::lib ) endfunction() add_example(z_get) add_example(z_pub_st) add_example(z_pub) add_example(z_pull) add_example(z_put) add_example(z_queryable) add_example(z_scout) add_example(z_sub_st) add_example(z_sub) ================================================ FILE: examples/freertos_plus_tcp/include/FreeRTOSConfig.h ================================================ // // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // Błażej Sowa, #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #include "bits/pthread_stack_min.h" /* Core options */ #define configUSE_PREEMPTION 1 #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES (56) #define configMINIMAL_STACK_SIZE ((uint16_t)PTHREAD_STACK_MIN) #define configMAX_TASK_NAME_LEN (16) #define configUSE_16_BIT_TICKS 0 #define configQUEUE_REGISTRY_SIZE 0 #define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 /* Hook function related definitions. */ #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 #define configUSE_SB_COMPLETED_CALLBACK 0 /* Enable/Disable features */ #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configUSE_CO_ROUTINES 0 #define configUSE_TIMERS 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_QUEUE_SETS 0 #define configUSE_NEWLIB_REENTRANT 0 /* Set the API functions which should be included */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskCleanUpResources 0 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xQueueGetMutexHolder 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xTimerPendFunctionCall 0 #endif /* FREERTOS_CONFIG_H */ ================================================ FILE: examples/freertos_plus_tcp/include/FreeRTOSIPConfig.h ================================================ // // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // Błażej Sowa, #ifndef FREERTOS_IP_CONFIG_H #define FREERTOS_IP_CONFIG_H // Driver specific #define ipconfigBYTE_ORDER pdFREERTOS_LITTLE_ENDIAN #define ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES 1 #define ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM 0 #define ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM 1 #define ipconfigZERO_COPY_RX_DRIVER 1 #define ipconfigZERO_COPY_TX_DRIVER 1 #define ipconfigUSE_LINKED_RX_MESSAGES 1 #define ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS 60 // Enable/Disable features #define ipconfigUSE_IPv4 1 #define ipconfigUSE_IPv6 1 #define ipconfigUSE_DHCP 1 #define ipconfigUSE_DHCPv6 0 #define ipconfigUSE_RA 0 #define ipconfigUSE_DNS 1 #define ipconfigUSE_TCP 1 #define ipconfigUSE_ARP_REMOVE_ENTRY 0 #define ipconfigUSE_ARP_REVERSED_LOOKUP 0 #define ipconfigSUPPORT_SELECT_FUNCTION 1 #define ipconfigSUPPORT_OUTGOING_PINGS 0 #define ipconfigUSE_NETWORK_EVENT_HOOK 1 #define ipconfigUSE_DHCP_HOOK 0 // DHCP #define ipconfigMAXIMUM_DISCOVER_TX_PERIOD (pdMS_TO_TICKS(1000U)) #define ipconfigDHCP_REGISTER_HOSTNAME 1 #define ipconfigIP_TASK_PRIORITY (configMAX_PRIORITIES - 2) #define ipconfigIP_TASK_STACK_SIZE_WORDS (configMINIMAL_STACK_SIZE * 5) // Set ipconfigBUFFER_PADDING on 64-bit platforms #if INTPTR_MAX == INT64_MAX #define ipconfigBUFFER_PADDING 14U #endif #endif /* FREERTOS_IP_CONFIG_H */ ================================================ FILE: examples/freertos_plus_tcp/main.c ================================================ // // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // Błażej Sowa, #include #include #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "task.h" #define APP_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE #define APP_TASK_PRIORITY 10 static const uint8_t ucIPAddress[] = {192, 168, 1, 80}; static const uint8_t ucNetMask[] = {255, 255, 255, 0}; static const uint8_t ucGatewayAddress[] = {192, 168, 1, 1}; static const uint8_t ucDNSServerAddress[] = {192, 168, 1, 1}; static const uint8_t ucMACAddress[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; NetworkInterface_t *pxLibslirp_FillInterfaceDescriptor(BaseType_t xEMACIndex, NetworkInterface_t *pxInterface); static NetworkInterface_t xInterface; static NetworkEndPoint_t xEndPoint; static TaskHandle_t xAppTaskHandle; static StaticTask_t xAppTaskTCB; static StackType_t uxAppTaskStack[APP_TASK_STACK_DEPTH]; void app_main(); static void vAppTask(void * /*argument*/) { printf("Waiting for network...\n"); uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000)); if (ulNotificationValue == 0) { printf("Timed out waiting for network.\n"); } else { printf("Starting Zenoh App...\n"); app_main(); } vTaskDelete(NULL); } int main(int argc, char **argv) { memcpy(ipLOCAL_MAC_ADDRESS, ucMACAddress, sizeof(ucMACAddress)); pxLibslirp_FillInterfaceDescriptor(0, &xInterface); FreeRTOS_FillEndPoint(&xInterface, &xEndPoint, ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress); xEndPoint.bits.bWantDHCP = 1; FreeRTOS_IPInit_Multi(); xAppTaskHandle = xTaskCreateStatic(vAppTask, "", APP_TASK_STACK_DEPTH, NULL, APP_TASK_PRIORITY, uxAppTaskStack, &xAppTaskTCB); vTaskStartScheduler(); return 0; } void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) { static StaticTask_t xIdleTaskTCB; static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE]; *ppxIdleTaskTCBBuffer = &xIdleTaskTCB; *ppxIdleTaskStackBuffer = uxIdleTaskStack; *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; } BaseType_t xApplicationGetRandomNumber(uint32_t *pulNumber) { *pulNumber = (uint32_t)rand(); return pdTRUE; } uint32_t ulApplicationGetNextSequenceNumber(uint32_t /*ulSourceAddress*/, uint16_t /*usSourcePort*/, uint32_t /*ulDestinationAddress*/, uint16_t /*usDestinationPort*/) { uint32_t ulNext = 0; xApplicationGetRandomNumber(&ulNext); return ulNext; } const char *pcApplicationHostnameHook(void) { return "FreeRTOS-simulator"; } void vApplicationIPNetworkEventHook_Multi(eIPCallbackEvent_t eNetworkEvent, struct xNetworkEndPoint * /*xEndPoint*/) { if (eNetworkEvent == eNetworkUp) { printf("Network is up!\n"); uint32_t ulIPAddress = 0; uint32_t ulNetMask = 0; uint32_t ulGatewayAddress = 0; uint32_t ulDNSServerAddress = 0; char cBuf[50]; // The network is up and configured. Print out the configuration obtained // from the DHCP server. FreeRTOS_GetEndPointConfiguration(&ulIPAddress, &ulNetMask, &ulGatewayAddress, &ulDNSServerAddress, &xEndPoint); // Convert the IP address to a string then print it out. FreeRTOS_inet_ntoa(ulIPAddress, cBuf); printf("IP Address: %s\n", cBuf); // Convert the net mask to a string then print it out. FreeRTOS_inet_ntoa(ulNetMask, cBuf); printf("Subnet Mask: %s\n", cBuf); // Convert the IP address of the gateway to a string then print it out. FreeRTOS_inet_ntoa(ulGatewayAddress, cBuf); printf("Gateway IP Address: %s\n", cBuf); // Convert the IP address of the DNS server to a string then print it out. FreeRTOS_inet_ntoa(ulDNSServerAddress, cBuf); printf("DNS server IP Address: %s\n", cBuf); // Make sure MAC address of the gateway is known if (xARPWaitResolution(ulGatewayAddress, pdMS_TO_TICKS(3000)) < 0) { xTaskNotifyGive(xAppTaskHandle); } else { printf("Failed to obtain the MAC address of the gateway!\n"); } } else if (eNetworkEvent == eNetworkDown) { printf("IPv4 End Point is down!\n"); } } ================================================ FILE: examples/freertos_plus_tcp/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include "FreeRTOS.h" #if Z_FEATURE_QUERY == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" #define VALUE "" void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(">> Received an error\n"); } } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, KEYEXPR) < 0) { printf("%s is not a valid key expression\n", KEYEXPR); return; } while (1) { z_sleep_s(5); printf("Sending Query '%s'...\n", KEYEXPR); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_move(payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return; } } z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include "FreeRTOS.h" #if Z_FEATURE_PUBLICATION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[FreeRTOS-Plus-TCP] Pub from Zenoh-Pico!" void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring publisher for '%s'...\n", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return; } char *buf = (char *)pvPortMalloc(256); for (int idx = 0; 1; ++idx) { z_sleep_s(1); snprintf(buf, 256, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put_options_t options; z_publisher_put_options_default(&options); z_publisher_put(z_loan(pub), z_move(payload), &options); } // Clean-up z_drop(z_move(pub)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_pub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include "FreeRTOS.h" #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 0 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[FreeRTOS-Plus-TCP] Pub from Zenoh-Pico!" #define N 2147483647 // max int value by default void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring publisher for '%s'...\n", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return; } char *buf = (char *)pvPortMalloc(256); z_clock_t now = z_clock_now(); for (int idx = 0; idx < N;) { if (z_clock_elapsed_ms(&now) > 1000) { snprintf(buf, 256, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); ++idx; now = z_clock_now(); } z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(pub)); z_drop(z_move(s)); } #else void app_main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_PUBLICATION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, SIZE); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } printf("Pulling data every %zu ms... Ring size: %zd\n", INTERVAL, SIZE); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", INTERVAL); z_sleep_ms(INTERVAL); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_put.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #if Z_FEATURE_PUBLICATION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-put" #define VALUE "[FreeRTOS-Plus-TCP] Pub from Zenoh-Pico!" void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring key expression '%s'...\n", KEYEXPR); z_owned_keyexpr_t ke; z_view_keyexpr_t vke; z_view_keyexpr_from_str_unchecked(&vke, KEYEXPR); if (z_declare_keyexpr(z_loan(s), &ke, z_loan(vke)) < 0) { printf("Unable to declare key expression!\n"); z_drop(z_move(s)); return; } printf("Putting Data ('%s': '%s')...\n", KEYEXPR, VALUE); z_put_options_t options; z_put_options_default(&options); // Create payload z_owned_bytes_t payload; z_bytes_from_static_str(&payload, VALUE); if (z_put(z_loan(s), z_loan(ke), z_move(payload), &options) < 0) { printf("Oh no! Put has failed...\n"); } while (1) { z_sleep_s(1); } // Clean up z_undeclare_keyexpr(z_loan(s), z_move(ke)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #if Z_FEATURE_QUERYABLE == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[FreeRTOS-Plus-TCP] Queryable from Zenoh-Pico!" void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); z_query_reply_options_t options; z_query_reply_options_default(&options); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), &options); } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, KEYEXPR) < 0) { printf("%s is not a valid key expression\n", KEYEXPR); return; } printf("Creating Queryable on '%s'...\n", KEYEXPR); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return; } while (1) { z_sleep_s(1); } z_drop(z_move(qable)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include #include #include #include #include "FreeRTOS.h" #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; vPortFree(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } void app_main(void) { int *context = (int *)pvPortMalloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_move(config), z_move(closure), NULL); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } while (1) { z_sleep_s(1); } z_drop(z_move(sub)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); } #endif ================================================ FILE: examples/freertos_plus_tcp/z_sub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 0 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" #define N 2147483647 // max int value by default int msg_nb = 0; void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } printf("Running until %d messages are received...\n", N); while (msg_nb < N) { z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(sub)); z_drop(z_move(s)); } #else void app_main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_SUBSCRIPTION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); } #endif ================================================ FILE: examples/mbed/z_get.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_QUERY == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" #define VALUE "" void reply_dropper(void *ctx) { printf(" >> Received query final notification\n"); } void reply_handler(z_loaned_reply_t *oreply, void *ctx) { if (z_reply_is_ok(oreply)) { const z_loaned_sample_t *sample = z_reply_ok(oreply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(" >> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&replystr)), z_string_data(z_string_loan(&replystr))); z_string_drop(z_string_move(&replystr)); } else { printf(" >> Received an error\n"); } } int main(int argc, char **argv) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); while (1) { z_sleep_s(5); printf("Sending Query '%s'...\n", KEYEXPR); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_bytes_move(&payload); } z_owned_closure_reply_t callback; z_closure_reply(&callback, reply_handler, reply_dropper, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_get(z_session_loan(&s), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&callback), &opts) < 0) { printf("Unable to send query.\n"); exit(-1); } } printf("Closing Zenoh Session..."); z_session_drop(z_session_move(&s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it\n"); return -2; } #endif ================================================ FILE: examples/mbed/z_pub.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[MBedOS]{nucleo-F767ZI} Pub from Zenoh-Pico!" int main(int argc, char **argv) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring publisher for '%s'...", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_session_loan(&s), &pub, z_view_keyexpr_loan(&ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); exit(-1); } printf("OK\n"); char buf[256]; for (int idx = 0; 1; ++idx) { z_sleep_s(1); sprintf(buf, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL); } printf("Closing Zenoh Session..."); z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/mbed/z_pull.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; int main(int argc, char **argv) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, SIZE); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Pulling data every %zu ms... Ring size: %zd\n", INTERVAL, SIZE); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample); res == Z_OK; res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_sample_loan(&sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_sample_loan(&sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); z_sample_drop(z_sample_move(&sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", INTERVAL); z_sleep_ms(INTERVAL); } else { break; } } z_subscriber_drop(z_subscriber_move(&sub)); z_ring_handler_sample_drop(z_ring_handler_sample_move(&handler)); z_session_drop(z_session_move(&s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/mbed/z_queryable.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_QUERYABLE == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[MBedOS]{nucleo-F767ZI} Queryable from Zenoh-Pico!" void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_view_string_loan(¶ms)), z_string_data(z_view_string_loan(¶ms))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_string_loan(&payload_string)) > 1) { printf(" with value '%.*s'\n", (int)z_string_len(z_string_loan(&payload_string)), z_string_data(z_string_loan(&payload_string))); } z_string_drop(z_string_move(&payload_string)); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_query_keyexpr(query), z_bytes_move(&reply_payload), NULL); } int main(int argc, char **argv) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); // Declare Zenoh queryable printf("Declaring Queryable on %s...", KEYEXPR); z_owned_closure_query_t callback; z_closure_query(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_queryable(z_session_loan(&s), &qable, z_view_keyexpr_loan(&ke), z_closure_query_move(&callback), NULL) < 0) { printf("Unable to declare queryable.\n"); exit(-1); } printf("OK\n"); printf("Zenoh setup finished!\n"); while (1) { z_sleep_s(1); } printf("Closing Zenoh Session..."); z_queryable_drop(z_queryable_move(&qable)); z_session_drop(z_session_move(&s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/mbed/z_scout.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_SCOUTING == 1 uint8_t zid_len(z_id_t id) { uint8_t len = 16; while (len > 0) { --len; if (id.id[len] != 0) { ++len; break; } } return len; } void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = zid_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_view_string_loan(&s)), z_string_data(z_view_string_loan(&s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } int main(void) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure_hello(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_config_move(&config), z_closure_hello_move(&closure), NULL); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/mbed/z_sub.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" void data_handler(z_loaned_sample_t *sample, void *arg) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(" >> [Subscriber handler] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); } int main(int argc, char **argv) { randLIB_seed_random(); EthernetInterface net; net.set_network("192.168.11.2", "255.255.255.0", "192.168.11.1"); net.connect(); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } printf("OK\n"); printf("Declaring Subscriber on '%s'...", KEYEXPR); z_owned_closure_sample_t callback; z_closure_sample(&callback, data_handler, NULL, NULL); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); exit(-1); } printf("OK!\n"); while (1) { z_sleep_s(1); } printf("Closing Zenoh Session..."); z_subscriber_drop(z_subscriber_move(&sub)); z_session_drop(z_session_move(&s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/packages/zenohpico-mylinux/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.14) project(zenohpico_mylinux LANGUAGES C) include(GNUInstallDirs) set(ZENOH_PICO_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../.." CACHE PATH "Path to the zenoh-pico source tree") get_filename_component(ZENOH_PICO_SOURCE_DIR "${ZENOH_PICO_SOURCE_DIR}" ABSOLUTE) set(ZENOH_PICO_CONFIG_INCLUDE_DIR "${ZENOH_PICO_SOURCE_DIR}/build/include" CACHE PATH "Path to the configured zenoh-pico include directory with zenoh-pico/config.h") get_filename_component(ZENOH_PICO_CONFIG_INCLUDE_DIR "${ZENOH_PICO_CONFIG_INCLUDE_DIR}" ABSOLUTE) if(NOT EXISTS "${ZENOH_PICO_SOURCE_DIR}/include/zenoh-pico.h") message(FATAL_ERROR "ZENOH_PICO_SOURCE_DIR must point to a zenoh-pico source tree; " "missing include/zenoh-pico.h in ${ZENOH_PICO_SOURCE_DIR}") endif() if(NOT EXISTS "${ZENOH_PICO_CONFIG_INCLUDE_DIR}/zenoh-pico/config.h") message(FATAL_ERROR "ZENOH_PICO_CONFIG_INCLUDE_DIR must point to a configured zenoh-pico include tree; " "missing zenoh-pico/config.h in ${ZENOH_PICO_CONFIG_INCLUDE_DIR}") endif() add_library(mylinux_system STATIC "${ZENOH_PICO_SOURCE_DIR}/src/system/unix/system.c") set_target_properties(mylinux_system PROPERTIES POSITION_INDEPENDENT_CODE ON EXPORT_NAME system) target_include_directories(mylinux_system PUBLIC $ $ PRIVATE "${ZENOH_PICO_CONFIG_INCLUDE_DIR}" "${ZENOH_PICO_SOURCE_DIR}/include") target_compile_definitions(mylinux_system PRIVATE ZENOH_MYLINUX ZP_SYSTEM_PLATFORM_HEADER=\"zenoh_mylinux_platform.h\") add_library(mylinux_serial_uart STATIC "${ZENOH_PICO_SOURCE_DIR}/src/link/transport/serial/tty_posix.c") set_target_properties(mylinux_serial_uart PROPERTIES POSITION_INDEPENDENT_CODE ON EXPORT_NAME serial_uart) target_include_directories(mylinux_serial_uart PRIVATE "${ZENOH_PICO_CONFIG_INCLUDE_DIR}" "${ZENOH_PICO_SOURCE_DIR}/include" "${CMAKE_CURRENT_LIST_DIR}/include") target_compile_definitions(mylinux_serial_uart PRIVATE ZENOH_MYLINUX ZP_SYSTEM_PLATFORM_HEADER=\"zenoh_mylinux_platform.h\") install(TARGETS mylinux_system mylinux_serial_uart EXPORT zenohpicoMylinuxTargets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") install(EXPORT zenohpicoMylinuxTargets NAMESPACE mylinux:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zenohpico-mylinux") install(FILES "${CMAKE_CURRENT_LIST_DIR}/include/zenoh_mylinux_platform.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") install(FILES "${CMAKE_CURRENT_LIST_DIR}/cmake/zenohpico-mylinuxConfig.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zenohpico-mylinux") install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/cmake/platforms" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/zenohpico-mylinux") ================================================ FILE: examples/packages/zenohpico-mylinux/README.md ================================================ # zenohpico-mylinux This directory contains a full out-of-tree package example for Zenoh-Pico. It exports: - `mylinux::system` - `mylinux::serial_uart` and provides: - `platforms/mylinux.cmake` The example defines one `mylinux` platform profile. The profile sets one `ZP_PLATFORM_SOURCE_FILES` list with the reused POSIX network/TCP/UDP sources, adds the package include directory for `zenoh_mylinux_platform.h`, and links the exported `mylinux::system` and `mylinux::serial_uart` targets through `ZP_PLATFORM_LINK_LIBRARIES`. ## Package layout ```text examples/packages/zenohpico-mylinux/ CMakeLists.txt include/ zenoh_mylinux_platform.h cmake/ zenohpico-mylinuxConfig.cmake platforms/ mylinux.cmake consumer/ CMakeLists.txt main.c ``` ## What each file does - `CMakeLists.txt` builds and installs the exported package targets `mylinux::system` and `mylinux::serial_uart`, and installs the custom platform header. - `include/zenoh_mylinux_platform.h` defines the platform-specific types used when `ZENOH_MYLINUX` is selected. - `cmake/zenohpico-mylinuxConfig.cmake` loads the exported targets file and registers the package platform descriptor directory. - `cmake/platforms/mylinux.cmake` defines the `mylinux` platform profile with `ZP_PLATFORM_*` variables. - `consumer/` is a minimal downstream project that uses an installed `zenohpico::static`. ## Build and install the package From the Zenoh-Pico source tree root: ```bash cmake -S . -B /tmp/zenohpico-config \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF cmake -S examples/packages/zenohpico-mylinux -B /tmp/zenohpico-mylinux-pkg \ -DZENOH_PICO_SOURCE_DIR="$PWD" \ -DZENOH_PICO_CONFIG_INCLUDE_DIR=/tmp/zenohpico-config/include cmake --build /tmp/zenohpico-mylinux-pkg -j cmake --install /tmp/zenohpico-mylinux-pkg --prefix /tmp/zenohpico-mylinux-prefix ``` ## Build Zenoh-Pico with the package ```bash cmake -S . -B /tmp/zenohpico-mylinux-build \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_TESTING=OFF \ -DZP_EXTERNAL_PACKAGES=zenohpico-mylinux \ -DCMAKE_PREFIX_PATH=/tmp/zenohpico-mylinux-prefix \ -DZP_PLATFORM=mylinux cmake --build /tmp/zenohpico-mylinux-build -j --target zenohpico_static cmake --install /tmp/zenohpico-mylinux-build --prefix /tmp/zenohpico-install ``` ## Build a downstream consumer After installing Zenoh-Pico itself, the `consumer/` subdirectory can be used as a minimal downstream project: ```bash cmake -S examples/packages/zenohpico-mylinux/consumer -B /tmp/zenohpico-mylinux-consumer \ -DCMAKE_PREFIX_PATH="/tmp/zenohpico-install;/tmp/zenohpico-mylinux-prefix" cmake --build /tmp/zenohpico-mylinux-consumer -j ``` ================================================ FILE: examples/packages/zenohpico-mylinux/cmake/platforms/mylinux.cmake ================================================ set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MYLINUX) set(ZP_PLATFORM_SYSTEM_PLATFORM_HEADER "zenoh_mylinux_platform.h") set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/unix/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_posix.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_posix.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_posix.c") endif() list(APPEND ZP_PLATFORM_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../../../include") list(APPEND ZP_PLATFORM_LINK_LIBRARIES mylinux::system mylinux::serial_uart) ================================================ FILE: examples/packages/zenohpico-mylinux/cmake/zenohpico-mylinuxConfig.cmake ================================================ include("${CMAKE_CURRENT_LIST_DIR}/zenohpicoMylinuxTargets.cmake") if(COMMAND zp_add_platform_dir) zp_add_platform_dir("${CMAKE_CURRENT_LIST_DIR}/platforms") endif() ================================================ FILE: examples/packages/zenohpico-mylinux/consumer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.14) project(zenohpico_mylinux_consumer LANGUAGES C) find_package(zenohpico CONFIG REQUIRED) add_executable(consumer main.c) target_link_libraries(consumer PRIVATE zenohpico::static) ================================================ FILE: examples/packages/zenohpico-mylinux/consumer/main.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include int main(void) { return 0; } ================================================ FILE: examples/packages/zenohpico-mylinux/include/zenoh_mylinux_platform.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_EXAMPLES_MYLINUX_PLATFORM_H #define ZENOH_PICO_EXAMPLES_MYLINUX_PLATFORM_H #include #include #include #include #if Z_FEATURE_MULTI_THREAD == 1 #include #endif #include "zenoh-pico/config.h" #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 #include #endif #ifdef __cplusplus extern "C" { #endif #define ZP_PLATFORM_SOCKET_POSIX 1 #if Z_FEATURE_MULTI_THREAD == 1 typedef pthread_t _z_task_t; typedef pthread_attr_t z_task_attr_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef pthread_t _z_task_id_t; #endif typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 || \ Z_FEATURE_RAWETH_TRANSPORT == 1 || Z_FEATURE_LINK_SERIAL == 1 int _fd; #endif }; #if Z_FEATURE_LINK_TLS == 1 void *_tls_sock; #endif } _z_sys_net_socket_t; typedef struct { union { #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_EXAMPLES_MYLINUX_PLATFORM_H */ ================================================ FILE: examples/platforms/myplatform.cmake ================================================ # Minimal one-file platform descriptor template. # # Copy this file to: # - cmake/platforms/.cmake for an in-tree platform profile, or # - /platforms/.cmake for an external package. # # Replace the paths and names below with the files and libraries from your # platform. # Minimal required set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_MYPLATFORM) set(ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/system/myplatform/system.c" "${PROJECT_SOURCE_DIR}/src/system/myplatform/network.c" "${PROJECT_SOURCE_DIR}/src/link/transport/tcp/tcp_myplatform.c" "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_myplatform.c" "${PROJECT_SOURCE_DIR}/src/link/transport/serial/uart_myplatform.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/link/transport/udp/udp_multicast_myplatform.c") endif() # Optional, when the platform provides a BT transport implementation. # list(APPEND ZP_PLATFORM_SOURCE_FILES # "${PROJECT_SOURCE_DIR}/src/link/transport/bt/bt_myplatform.c") # Optional # Only when the logical system-layer name differs from the profile name. # set(ZP_PLATFORM_SYSTEM_LAYER arduino_opencr) # Only when include/zenoh-pico/system/common/platform.h should include a # custom header instead of a built-in one. # set(ZP_PLATFORM_SYSTEM_PLATFORM_HEADER "zenoh_myplatform_platform.h") # list(APPEND ZP_PLATFORM_SOURCE_FILES # "${PROJECT_SOURCE_DIR}/src/system/myplatform/platform_extra.c") # list(APPEND ZP_PLATFORM_INCLUDE_DIRS # "${PROJECT_SOURCE_DIR}/src/system/myplatform/include") # list(APPEND ZP_PLATFORM_COMPILE_DEFINITIONS # ZENOH_MYPLATFORM_BOARD) # list(APPEND ZP_PLATFORM_COMPILE_OPTIONS # -Wall) # list(APPEND ZP_PLATFORM_LINK_LIBRARIES # myplatform::sdk) ================================================ FILE: examples/rpi_pico/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.13) set(PICO_BOARD "pico_w" CACHE STRING "Raspberry Pi Pico board: pico, pico_w, pico2, pico2_w") set(WIFI_SUPPORT_ENABLED 0) if(PICO_BOARD STREQUAL "pico_w" OR PICO_BOARD STREQUAL "pico2_w") set(WIFI_SUPPORT_ENABLED 1) endif() # Set options set(WIFI_SSID "" CACHE STRING "WiFi SSID") set(WIFI_PASSWORD "" CACHE STRING "WiFi Password") set(ZENOH_CONFIG_MODE "client" CACHE STRING "ZENOH_CONFIG_MODE") set(ZENOH_CONFIG_CONNECT CACHE STRING "ZENOH_CONFIG_CONNECT") set(ZENOH_CONFIG_LISTEN CACHE STRING "ZENOH_CONFIG_LISTEN") option(ZENOH_USB_UART "Enable USB UART" OFF) message(STATUS "PICO_BOARD: ${PICO_BOARD}") message(STATUS "WIFI_SSID: ${WIFI_SSID}") if(WIFI_PASSWORD STREQUAL "") message(STATUS "WIFI_PASSWORD is empty") else() message(STATUS "WIFI_PASSWORD is set") endif() message(STATUS "ZENOH_CONFIG_MODE: ${ZENOH_CONFIG_MODE}") message(STATUS "ZENOH_CONFIG_CONNECT: ${ZENOH_CONFIG_CONNECT}") message(STATUS "ZENOH_CONFIG_LISTEN: ${ZENOH_CONFIG_LISTEN}") message(STATUS "ZENOH_USB_UART: ${ZENOH_USB_UART}") configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/include/config.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/include/config.h" ) set(CMAKE_C_STANDARD 11) # Include Raspberry Pi Pico SDK if(NOT DEFINED ENV{PICO_SDK_PATH}) message(FATAL_ERROR "PICO_SDK_PATH environment variable is not set. Please set it to the location of the Pico SDK.") endif() include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) # Include FreeRTOS SDK include(FreeRTOS_Kernel_import.cmake) # Configure project project(zenohpico_rpi_pico_examples C CXX ASM) if(WIFI_SUPPORT_ENABLED) set(WIFI_LIB pico_cyw43_arch_lwip_sys_freertos) else() set(WIFI_LIB "") endif() if(ZENOH_USB_UART) set(USB_LIBS tinyusb_host tinyusb_device tinyusb_board ) set(USB_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/include/tusb ) else() set(USB_LIBS "") set(USB_INCLUDE "") endif() add_compile_definitions(LWIP_TIMEVAL_PRIVATE=0) pico_sdk_init() # Include Zenoh Pico library include(../../cmake/helpers.cmake) set_default_build_type(Release) if (NOT WIFI_SUPPORT_ENABLED) declare_cache_var(Z_FEATURE_LINK_TCP 0 STRING "TCP support") declare_cache_var(Z_FEATURE_LINK_UDP_MULTICAST 0 STRING "UDP multicast support") declare_cache_var(Z_FEATURE_LINK_UDP_UNICAST 0 STRING "UDP unicast support") declare_cache_var(Z_FEATURE_SCOUTING 0 STRING "Scouting support") endif() declare_cache_var(Z_FEATURE_LINK_SERIAL 1 STRING "Serial support") if(ZENOH_USB_UART) declare_cache_var(Z_FEATURE_UNSTABLE_API 1 STRING "Enable unstable API") declare_cache_var(Z_FEATURE_LINK_SERIAL_USB 1 STRING "Serial USB support") else() declare_cache_var(Z_FEATURE_LINK_SERIAL_USB 0 STRING "Serial USB support") endif() set(BUILD_SHARED_LIBS OFF) set(ZP_PLATFORM "rpi_pico" CACHE STRING "Zenoh-Pico platform profile") configure_include_project(ZENOHPICO zenohpico zenohpico::lib "../.." zenohpico "https://github.com/eclipse-zenoh/zenoh-pico" "") target_link_libraries(zenohpico_static PRIVATE hardware_uart pico_stdlib pico_rand FreeRTOS-Kernel-Heap4 ${WIFI_LIB} ${USB_LIBS} ) # Configure build target_include_directories(zenohpico_static PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/include ${USB_INCLUDE} ) add_library(main STATIC main.c) target_include_directories(main PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/include ) target_link_libraries(main pico_stdlib pico_rand FreeRTOS-Kernel-Heap4 ${WIFI_LIB} ) function(add_example name) add_executable(${name} ${name}.c) target_link_libraries(${name} main hardware_uart pico_stdlib pico_rand FreeRTOS-Kernel-Heap4 zenohpico::lib ${WIFI_LIB} ${USB_LIBS} ) target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/include ${USB_INCLUDE} ) if(ZENOH_USB_UART) pico_enable_stdio_uart(${name} 1) pico_enable_stdio_usb(${name} 0) else() pico_enable_stdio_uart(${name} 0) pico_enable_stdio_usb(${name} 1) endif() pico_add_extra_outputs(${name}) endfunction() add_example(z_get) add_example(z_pub) add_example(z_pub_st) add_example(z_pub_thr) add_example(z_pull) add_example(z_put) add_example(z_queryable) add_example(z_scout) add_example(z_sub) add_example(z_sub_st) add_example(z_sub_thr) ================================================ FILE: examples/rpi_pico/FreeRTOS_Kernel_import.cmake ================================================ # This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake # This can be dropped into an external project to help locate the FreeRTOS kernel # It should be include()ed prior to project(). Alternatively this file may # or the CMakeLists.txt in this directory may be included or added via add_subdirectory # respectively. if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") endif () # first pass we look in old tree; second pass we look in new tree foreach(SEARCH_PASS RANGE 0 1) if (SEARCH_PASS) # ports may be moving to submodule in the future set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/Community-Supported-Ports/GCC") set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../../..") else() set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC") set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") endif() if(PICO_PLATFORM STREQUAL "rp2040") set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2040") else() if (PICO_PLATFORM STREQUAL "rp2350-riscv") set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_RISC-V") else() set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/RP2350_ARM_NTZ") endif() endif() if (NOT FREERTOS_KERNEL_PATH) # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) endif() if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") break() elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") break() endif() endif () if (NOT FREERTOS_KERNEL_PATH) foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) # check if FreeRTOS-Kernel exists under directory that included us set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") break() endif() endforeach() if (FREERTOS_KERNEL_PATH) break() endif() endif() # user must have specified if (FREERTOS_KERNEL_PATH) if (EXISTS "${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") break() endif() endif() endforeach () if (NOT FREERTOS_KERNEL_PATH) message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") endif() set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") endif() if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain a '${PICO_PLATFORM}' port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") endif() set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) ================================================ FILE: examples/rpi_pico/include/FreeRTOSConfig.h ================================================ /* * FreeRTOS V202111.00 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://www.FreeRTOS.org * http://aws.amazon.com/freertos * * 1 tab == 4 spaces! */ #ifndef FREERTOS_CONFIG_EXAMPLES_COMMON_H #define FREERTOS_CONFIG_EXAMPLES_COMMON_H /*----------------------------------------------------------- * Application specific definitions. * * These definitions should be adjusted for your particular hardware and * application requirements. * * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. * * See http://www.freertos.org/a00110.html *----------------------------------------------------------*/ /* Scheduler Related */ #define configUSE_PREEMPTION 1 #define configUSE_TICKLESS_IDLE 0 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configTICK_RATE_HZ ((TickType_t)1000) #define configMAX_PRIORITIES 32 #define configMINIMAL_STACK_SIZE (configSTACK_DEPTH_TYPE)512 #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 /* Synchronization Related */ #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_COUNTING_SEMAPHORES 1 #define configQUEUE_REGISTRY_SIZE 8 #define configUSE_QUEUE_SETS 1 #define configUSE_TIME_SLICING 1 #define configUSE_NEWLIB_REENTRANT 0 // todo need this for lwip FreeRTOS sys_arch to compile #define configENABLE_BACKWARD_COMPATIBILITY 1 #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 /* System */ #define configSTACK_DEPTH_TYPE uint32_t #define configMESSAGE_BUFFER_LENGTH_TYPE size_t /* Memory allocation related definitions. */ #define configSUPPORT_STATIC_ALLOCATION 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configTOTAL_HEAP_SIZE (128 * 1024) #define configAPPLICATION_ALLOCATED_HEAP 0 /* Hook function related definitions. */ #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 /* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 0 /* Co-routine related definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES 1 /* Software timer related definitions. */ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) #define configTIMER_QUEUE_LENGTH 10 #define configTIMER_TASK_STACK_DEPTH 1024 /* Interrupt nesting behaviour configuration. */ /* #define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] #define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] */ #define configNUMBER_OF_CORES 2 /* SMP (configNUMBER_OF_CORES > 1) only */ #define configTICK_CORE 0 #define configRUN_MULTIPLE_PRIORITIES 1 #if configNUMBER_OF_CORES > 1 #define configUSE_CORE_AFFINITY 1 #endif #define configUSE_PASSIVE_IDLE_HOOK 0 /* Armv8-M */ /* Not currently supported */ #define configENABLE_MPU 0 // #define configSYSTEM_CALL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 512 #define configENABLE_FPU 1 /* Not currently supported */ #define configENABLE_TRUSTZONE 0 #define configRUN_FREERTOS_SECURE_ONLY 1 // see https://www.freertos.org/RTOS-Cortex-M3-M4.html #define configMAX_SYSCALL_INTERRUPT_PRIORITY 16 /* RP2xxx specific */ #define configSUPPORT_PICO_SYNC_INTEROP 1 #define configSUPPORT_PICO_TIME_INTEROP 1 #include /* Define to trap errors during development. */ #define configASSERT(x) assert(x) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #define INCLUDE_vTaskPrioritySet 1 #define INCLUDE_uxTaskPriorityGet 1 #define INCLUDE_vTaskDelete 1 #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTaskGetCurrentTaskHandle 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_xTaskGetIdleTaskHandle 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xTaskAbortDelay 1 #define INCLUDE_xTaskGetHandle 1 #define INCLUDE_xTaskResumeFromISR 1 #define INCLUDE_xQueueGetMutexHolder 1 /* A header file that defines trace macro can be included here. */ #endif /* FREERTOS_CONFIG_H */ ================================================ FILE: examples/rpi_pico/include/config.h.in ================================================ #ifndef CONFIG_H #define CONFIG_H #define WIFI_SUPPORT_ENABLED @WIFI_SUPPORT_ENABLED@ #define WIFI_SSID "@WIFI_SSID@" #define WIFI_PASSWORD "@WIFI_PASSWORD@" #define ZENOH_CONFIG_MODE "@ZENOH_CONFIG_MODE@" #define ZENOH_CONFIG_CONNECT "@ZENOH_CONFIG_CONNECT@" #define ZENOH_CONFIG_LISTEN "@ZENOH_CONFIG_LISTEN@" #endif // CONFIG_H ================================================ FILE: examples/rpi_pico/include/lwipopts.h ================================================ #ifndef _LWIPOPTS_H #define _LWIPOPTS_H // Common settings used in most of the pico_w examples // (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details) #define NO_SYS 0 #define LWIP_SOCKET 1 #if PICO_CYW43_ARCH_POLL #define MEM_LIBC_MALLOC 1 #else // MEM_LIBC_MALLOC is incompatible with non polling versions #define MEM_LIBC_MALLOC 0 #endif #define MEM_ALIGNMENT 4 #define MEM_SIZE 4000 #define MEMP_NUM_TCP_SEG 64 #define MEMP_NUM_ARP_QUEUE 20 #define PBUF_POOL_SIZE 24 #define LWIP_ARP 1 #define LWIP_ETHERNET 1 #define LWIP_ICMP 1 #define LWIP_RAW 1 #define TCP_WND (16 * TCP_MSS) #define TCP_MSS 1460 #define TCP_SND_BUF (16 * TCP_MSS) #define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) #define LWIP_NETIF_STATUS_CALLBACK 1 #define LWIP_NETIF_LINK_CALLBACK 1 #define LWIP_NETIF_HOSTNAME 1 #define LWIP_NETCONN 0 #define MEM_STATS 0 #define SYS_STATS 0 #define MEMP_STATS 0 #define LINK_STATS 0 // #define ETH_PAD_SIZE 2 #define LWIP_CHKSUM_ALGORITHM 3 #define LWIP_DHCP 1 #define LWIP_IPV4 1 #define LWIP_TCP 1 #define LWIP_UDP 1 #define LWIP_DNS 1 #define LWIP_IGMP 1 #define LWIP_TCP_KEEPALIVE 1 #define LWIP_NETIF_TX_SINGLE_PBUF 1 #define DHCP_DOES_ARP_CHECK 0 #define LWIP_DHCP_DOES_ACD_CHECK 0 #define LWIP_SOCKET_POLL 0 #define LWIP_MULTICAST_TX_OPTIONS 1 #ifndef NDEBUG #define LWIP_DEBUG 1 #define LWIP_STATS 1 #define LWIP_STATS_DISPLAY 1 #define LWIP_FREERTOS_CHECK_CORE_LOCKING 1 #define LWIP_TCPIP_CORE_LOCKING 1 #define LWIP_TCPIP_CORE_LOCKING_INPUT 1 #endif #define ETHARP_DEBUG LWIP_DBG_OFF #define NETIF_DEBUG LWIP_DBG_OFF #define PBUF_DEBUG LWIP_DBG_OFF #define API_LIB_DEBUG LWIP_DBG_OFF #define API_MSG_DEBUG LWIP_DBG_OFF #define SOCKETS_DEBUG LWIP_DBG_OFF #define ICMP_DEBUG LWIP_DBG_OFF #define INET_DEBUG LWIP_DBG_OFF #define IP_DEBUG LWIP_DBG_OFF #define IP_REASS_DEBUG LWIP_DBG_OFF #define RAW_DEBUG LWIP_DBG_OFF #define MEM_DEBUG LWIP_DBG_OFF #define MEMP_DEBUG LWIP_DBG_OFF #define SYS_DEBUG LWIP_DBG_OFF #define TCP_DEBUG LWIP_DBG_OFF #define TCP_INPUT_DEBUG LWIP_DBG_OFF #define TCP_OUTPUT_DEBUG LWIP_DBG_OFF #define TCP_RTO_DEBUG LWIP_DBG_OFF #define TCP_CWND_DEBUG LWIP_DBG_OFF #define TCP_WND_DEBUG LWIP_DBG_OFF #define TCP_FR_DEBUG LWIP_DBG_OFF #define TCP_QLEN_DEBUG LWIP_DBG_OFF #define TCP_RST_DEBUG LWIP_DBG_OFF #define UDP_DEBUG LWIP_DBG_OFF #define TCPIP_DEBUG LWIP_DBG_OFF #define PPP_DEBUG LWIP_DBG_OFF #define SLIP_DEBUG LWIP_DBG_OFF #define DHCP_DEBUG LWIP_DBG_OFF #define TCP_LISTEN_BACKLOG 1 #define TCPIP_THREAD_STACKSIZE 2048 #define DEFAULT_THREAD_STACKSIZE 2048 #define DEFAULT_RAW_RECVMBOX_SIZE 16 #define DEFAULT_UDP_RECVMBOX_SIZE 16 #define DEFAULT_TCP_RECVMBOX_SIZE 16 #define DEFAULT_ACCEPTMBOX_SIZE 16 #define TCPIP_MBOX_SIZE 16 #define LWIP_TIMEVAL_PRIVATE 0 #define LWIP_SO_RCVTIMEO 1 #define LWIP_SO_SNDTIMEO 1 #define LWIP_SO_LINGER 1 #endif /* __LWIPOPTS_H__ */ ================================================ FILE: examples/rpi_pico/include/tusb/tusb_config.h ================================================ /* * The MIT License (MIT) * * Copyright (c) 2019 Ha Thach (tinyusb.org) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #ifndef _TUSB_CONFIG_H_ #define _TUSB_CONFIG_H_ #ifdef __cplusplus extern "C" { #endif //--------------------------------------------------------------------+ // Board Specific Configuration //--------------------------------------------------------------------+ // RHPort number used for device can be defined by board.mk, default to port 0 #ifndef BOARD_TUD_RHPORT #define BOARD_TUD_RHPORT 0 #endif // RHPort max operational speed can defined by board.mk #ifndef BOARD_TUD_MAX_SPEED #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED #endif //-------------------------------------------------------------------- // COMMON CONFIGURATION //-------------------------------------------------------------------- // defined by board.mk #ifndef CFG_TUSB_MCU #error CFG_TUSB_MCU must be defined #endif #ifndef CFG_TUSB_OS #define CFG_TUSB_OS OPT_OS_NONE #endif #ifndef CFG_TUSB_DEBUG #define CFG_TUSB_DEBUG 0 #endif // Enable Device stack #define CFG_TUD_ENABLED 1 // Default is max speed that hardware controller could support with on-chip PHY #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put * into those specific section. * e.g * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) */ #ifndef CFG_TUSB_MEM_SECTION #define CFG_TUSB_MEM_SECTION #endif #ifndef CFG_TUSB_MEM_ALIGN #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) #endif //-------------------------------------------------------------------- // DEVICE CONFIGURATION //-------------------------------------------------------------------- #ifndef CFG_TUD_ENDPOINT0_SIZE #define CFG_TUD_ENDPOINT0_SIZE 64 #endif //------------- CLASS -------------// #define CFG_TUD_CDC 2 #define CFG_TUD_MSC 0 #define CFG_TUD_HID 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 // CDC FIFO size of TX and RX #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) // CDC Endpoint transfer buffer size, more is faster #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } #endif #endif /* _TUSB_CONFIG_H_ */ ================================================ FILE: examples/rpi_pico/main.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "FreeRTOS.h" #include "config.h" #if WIFI_SUPPORT_ENABLED #include "pico/cyw43_arch.h" #endif #include "pico/stdlib.h" #include "task.h" #define TASK_PRIORITY (tskIDLE_PRIORITY + 2UL) #define WIFI_TIMEOUT 30000 int app_main(); #if WIFI_SUPPORT_ENABLED void print_ip_address() { struct netif *netif = &cyw43_state.netif[CYW43_ITF_STA]; if (netif_is_up(netif)) { printf("IP Address: %s\n", ip4addr_ntoa(netif_ip4_addr(netif))); printf("Netmask: %s\n", ip4addr_ntoa(netif_ip4_netmask(netif))); printf("Gateway: %s\n", ip4addr_ntoa(netif_ip4_gw(netif))); } else { printf("Network interface is down.\n"); } } #endif void main_task(void *params) { (void)params; #ifndef NDEBUG vTaskDelay(pdMS_TO_TICKS(3000)); #endif #if WIFI_SUPPORT_ENABLED if (cyw43_arch_init()) { printf("Failed to initialise\n"); return; } if (strlen(WIFI_SSID) != 0) { cyw43_arch_enable_sta_mode(); printf("Connecting to Wi-Fi...\n"); if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, WIFI_TIMEOUT) == 0) { printf("Wi-Fi connected.\n"); print_ip_address(); app_main(); } else { printf("Failed to connect Wi-Fi\n"); } } else { printf("Offline mode\n"); app_main(); } #else app_main(); #endif printf("Terminate.\n"); #if WIFI_SUPPORT_ENABLED cyw43_arch_deinit(); #endif vTaskDelete(NULL); } int main(void) { stdio_init_all(); xTaskCreate(main_task, "MainThread", configMINIMAL_STACK_SIZE * 16, NULL, TASK_PRIORITY, NULL); vTaskStartScheduler(); return 0; } ================================================ FILE: examples/rpi_pico/z_get.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_QUERY == 1 #define KEYEXPR "demo/example/**" #define VALUE "" void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(">> Received an error\n"); } } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, KEYEXPR) < 0) { printf("%s is not a valid key expression\n", KEYEXPR); return; } while (1) { z_sleep_s(5); printf("Sending Query '%s'...\n", KEYEXPR); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_move(payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return; } } z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_pub.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_PUBLICATION == 1 #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[RPI] Pub from Zenoh-Pico!" void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring publisher for '%s'...\n", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return; } // Publish data char buf[256]; for (int idx = 0; 1; ++idx) { z_sleep_s(1); snprintf(buf, 256, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put_options_t options; z_publisher_put_options_default(&options); z_publisher_put(z_loan(pub), z_move(payload), &options); } // Clean-up z_drop(z_move(pub)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_pub_st.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 0 #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[RPI] Pub from Zenoh-Pico!" void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring publisher for '%s'...\n", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return; } char *buf = (char *)pvPortMalloc(256); z_clock_t now = z_clock_now(); for (int idx = 0;; ++idx) { if (z_clock_elapsed_ms(&now) > 1000) { snprintf(buf, 256, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); ++idx; now = z_clock_now(); } z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(pub)); z_drop(z_move(s)); } #else void app_main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_PUBLICATION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_pub_thr.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_PUBLICATION == 1 #define KEYEXPR "test/thr" #define PAYLOAD_SIZE 8 void z_free_with_context(void *ptr, void *context) { (void)context; z_free(ptr); } void app_main(void) { uint8_t *value = (uint8_t *)z_malloc(PAYLOAD_SIZE); memset(value, 1, PAYLOAD_SIZE); // Set config z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } // Declare publisher z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return; } printf("Send packets\n"); z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, PAYLOAD_SIZE, z_free_with_context, NULL); while (1) { // Clone payload z_owned_bytes_t p; z_bytes_clone(&p, z_loan(payload)); z_publisher_put(z_loan(pub), z_move(p), NULL); } // Clean-up z_drop(z_move(pub)); z_drop(z_move(s)); z_drop(z_move(payload)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/rpi_pico/z_pull.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_SUBSCRIPTION == 1 #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, SIZE); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } printf("Pulling data every %zu ms... Ring size: %zd\n", INTERVAL, SIZE); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", INTERVAL); z_sleep_ms(INTERVAL); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_put.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_PUBLICATION == 1 #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[RPI] Pub from Zenoh-Pico!" void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } printf("Declaring key expression '%s'...\n", KEYEXPR); z_owned_keyexpr_t ke; z_view_keyexpr_t vke; z_view_keyexpr_from_str_unchecked(&vke, KEYEXPR); if (z_declare_keyexpr(z_loan(s), &ke, z_loan(vke)) < 0) { printf("Unable to declare key expression!\n"); z_drop(z_move(s)); return; } printf("Putting Data ('%s': '%s')...\n", KEYEXPR, VALUE); z_put_options_t options; z_put_options_default(&options); // Create payload z_owned_bytes_t payload; z_bytes_from_static_str(&payload, VALUE); if (z_put(z_loan(s), z_loan(ke), z_move(payload), &options) < 0) { printf("Oh no! Put has failed...\n"); } while (1) { z_sleep_s(1); } // Clean up z_undeclare_keyexpr(z_loan(s), z_move(ke)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_queryable.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_QUERYABLE == 1 #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[RPI] Queryable from Zenoh-Pico!" void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); z_query_reply_options_t options; z_query_reply_options_default(&options); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), &options); } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, KEYEXPR) < 0) { printf("%s is not a valid key expression\n", KEYEXPR); return; } printf("Creating Queryable on '%s'...\n", KEYEXPR); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return; } while (1) { z_sleep_s(1); } z_drop(z_move(qable)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_scout.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include "config.h" #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; z_free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } void app_main(void) { int *context = (int *)z_malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_move(config), z_move(closure), NULL); while (1) { z_sleep_s(1); } } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_sub.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_SUBSCRIPTION == 1 #define KEYEXPR "demo/example/**" void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } while (1) { z_sleep_s(1); } // Clean-up z_drop(z_move(sub)); z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_sub_st.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "config.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 0 #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[RPI] Pub from Zenoh-Pico!" int msg_nb = 0; void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } while (true) { z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(sub)); z_drop(z_move(s)); } #else void app_main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_SUBSCRIPTION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); } #endif ================================================ FILE: examples/rpi_pico/z_sub_thr.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "config.h" #if Z_FEATURE_SUBSCRIPTION == 1 #define KEYEXPR "test/thr" #define PACKET_NB 1000000 typedef struct { volatile unsigned long count; volatile unsigned long finished_rounds; z_clock_t start; z_clock_t first_start; } z_stats_t; z_stats_t *z_stats_make(void) { z_stats_t *stats = malloc(sizeof(z_stats_t)); stats->count = 0; stats->finished_rounds = 0; stats->first_start.tv_nsec = 0; return stats; } void on_sample(z_loaned_sample_t *sample, void *context) { (void)sample; z_stats_t *stats = (z_stats_t *)context; stats->count++; // Start set measurement if (stats->count == 1) { stats->start = z_clock_now(); if (stats->first_start.tv_nsec == 0) { stats->first_start = stats->start; } } else if (stats->count >= PACKET_NB) { // Stop set measurement stats->finished_rounds++; unsigned long elapsed_ms = z_clock_elapsed_ms(&stats->start); printf("%f msg/s\n", (double)(PACKET_NB * 1000) / (double)elapsed_ms); stats->count = 0; } } void drop_stats(void *context) { z_stats_t *stats = (z_stats_t *)context; unsigned long elapsed_ms = z_clock_elapsed_ms(&stats->first_start); const unsigned long sent_messages = PACKET_NB * stats->finished_rounds + stats->count; printf("Stats after unsubscribing: received %ld messages over %lu miliseconds (%.1f msg/s)\n", sent_messages, elapsed_ms, (double)(sent_messages * 1000) / (double)elapsed_ms); free(context); } void app_main(void) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, ZENOH_CONFIG_MODE); if (strcmp(ZENOH_CONFIG_CONNECT, "") != 0) { printf("Connect endpoint: %s\n", ZENOH_CONFIG_CONNECT); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, ZENOH_CONFIG_CONNECT); } if (strcmp(ZENOH_CONFIG_LISTEN, "") != 0) { printf("Listen endpoint: %s\n", ZENOH_CONFIG_LISTEN); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, ZENOH_CONFIG_LISTEN); } printf("Opening %s session ...\n", ZENOH_CONFIG_MODE); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return; } // Declare Subscriber/resource z_stats_t *context = z_stats_make(); z_owned_closure_sample_t callback; z_closure(&callback, on_sample, drop_stats, (void *)context); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_background_subscriber(z_loan(s), z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return; } while (1) { z_sleep_s(1); } // Clean-up z_drop(z_move(s)); } #else void app_main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); } #endif ================================================ FILE: examples/threadx_stm32/z_pub.c ================================================ /** ****************************************************************************** * @file z_pub.c * @brief Example zenoh-pico publisher on STM32 running ThreadX ****************************************************************************** */ #include #include "app_threadx.h" #include "zenoh-pico.h" #define MODE "client" #define LOCATOR "serial/Serial" #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[STM32 THREADX] Pub from Zenoh-Pico!" #define LOGGING 0 #if LOGGING == 1 #define _LOG(...) printf(__VA_ARGS__) #else #define _LOG(...) #endif /* Pointer used by system.c implementation to allocate from pool*/ TX_BYTE_POOL* pthreadx_byte_pool; VOID start_example_thread(ULONG initial_input) { z_owned_config_t config; z_owned_session_t s; z_result_t r = _Z_ERR_GENERIC; while (r != Z_OK) { // Wait until router is started z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } _LOG("Opening %s session ...\n", ZENOH_CONFIG_MODE); if ((r = z_open(&s, z_move(config), NULL)) < 0) { _LOG("Unable to open session!\n"); } } _LOG("Declaring publisher for '%s'...\n", KEYEXPR); z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { _LOG("Unable to declare publisher for key expression!\n"); return; } // Publish data while (1) { char buf[256]; for (int idx = 0; 1; ++idx) { z_sleep_s(1); snprintf(buf, 256, "[%4d] %s", idx, VALUE); _LOG("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put_options_t options; z_publisher_put_options_default(&options); z_publisher_put(z_loan(pub), z_move(payload), &options); } } // Clean-up z_drop(z_move(pub)); z_drop(z_move(s)); } TX_THREAD example_thread; UINT App_ThreadX_Init(VOID* memory_ptr) { UINT ret = TX_SUCCESS; TX_BYTE_POOL* byte_pool = (TX_BYTE_POOL*)memory_ptr; pthreadx_byte_pool = (TX_BYTE_POOL*)memory_ptr; void* pointer; /* Allocate the stack for the serial thread. */ tx_byte_allocate(byte_pool, &pointer, 4096, TX_NO_WAIT); /* Create the serial thread. */ tx_thread_create(&example_thread, "Zenoh-Pico-Example", start_example_thread, 0, pointer, 4096, 15, 15, TX_NO_TIME_SLICE, TX_AUTO_START); return ret; } void MX_ThreadX_Init(void) { tx_kernel_enter(); } ================================================ FILE: examples/threadx_stm32/z_sub.c ================================================ /** ****************************************************************************** * @file z_sub.c * @brief Example zenoh-pico subscriber on STM32 running ThreadX ****************************************************************************** */ #include #include "app_threadx.h" #include "zenoh-pico.h" #define MODE "client" #define LOCATOR "serial/Serial" #define KEYEXPR "demo/example/zenoh-pico-pub" #define LOGGING 0 #if LOGGING == 1 #define _LOG(...) printf(__VA_ARGS__) #else #define _LOG(...) #endif /* Pointer used by system.c implementation to allocate from pool*/ TX_BYTE_POOL *pthreadx_byte_pool; int msg_nb = 0; void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); _LOG(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } VOID start_example_thread(ULONG initial_input) { z_result_t r = _Z_ERR_GENERIC; z_owned_session_t s; z_owned_config_t config; while (r != Z_OK) { // Wait until router is started z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } _LOG("Opening %s session ...\n", ZENOH_CONFIG_MODE); if ((r = z_open(&s, z_move(config), NULL)) < 0) { _LOG("Unable to open session!\n"); } } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); _LOG("Declaring Subscriber on '%s'...\n", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { _LOG("Unable to declare subscriber.\n"); return; } while (1) { z_sleep_s(1); } // Clean-up z_drop(z_move(sub)); z_drop(z_move(s)); } TX_THREAD example_thread; UINT App_ThreadX_Init(VOID *memory_ptr) { UINT ret = TX_SUCCESS; TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL *)memory_ptr; pthreadx_byte_pool = (TX_BYTE_POOL *)memory_ptr; void *pointer; /* Allocate the stack for the serial thread. */ tx_byte_allocate(byte_pool, &pointer, 4096, TX_NO_WAIT); /* Create the serial thread. */ tx_thread_create(&example_thread, "Zenoh-Pico-Example", start_example_thread, 0, pointer, 4096, 15, 15, TX_NO_TIME_SLICE, TX_AUTO_START); return ret; } void MX_ThreadX_Init(void) { tx_kernel_enter(); } ================================================ FILE: examples/unix/c11/z_advanced_pub.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #if Z_FEATURE_ADVANCED_PUBLICATION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, size_t *history); int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-pub"; char *const default_value = "Pub from Pico!"; char *value = default_value; size_t history = 1; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &history); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif // Declare advanced publisher printf("Declaring AdvancedPublisher for '%s'...\n", keyexpr); ze_owned_advanced_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); pub_opts.cache.max_samples = history; pub_opts.cache.is_enabled = true; pub_opts.publisher_detection = true; ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); // or pub_opts.sample_miss_detection.is_enabled = true pub_opts.sample_miss_detection.is_enabled = true; pub_opts.sample_miss_detection.heartbeat_period_ms = 500; pub_opts.sample_miss_detection.heartbeat_mode = ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_PERIODIC; // if not set, publisher will retransmit samples based on periodic queries from advanced subscriber if (ze_declare_advanced_publisher(z_loan(s), &pub, z_loan(ke), &pub_opts) < 0) { printf("Unable to declare AdvancedPublisher for key expression!\n"); return -1; } // Publish data printf("Press CTRL-C to quit...\n"); char buf[256]; for (int idx = 0; 1; ++idx) { z_sleep_s(1); sprintf(buf, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); ze_advanced_publisher_put(z_loan(pub), z_move(payload), NULL); } z_drop(z_move(pub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, size_t *history) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:i:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'i': { int tmp = atoi(optarg); if (tmp < 0) { fprintf(stderr, "History size must be a positive integer.\n"); return 1; } *history = (size_t)tmp; break; } case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'i') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_ADVANCED_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_advanced_sub.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 static int parse_args(int argc, char** argv, z_owned_config_t* config, char** keyexpr); const char* kind_to_str(z_sample_kind_t kind); void data_handler(z_loaned_sample_t* sample, void* ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Advanced Subscriber] Received %s ('%.*s': '%.*s')\n", kind_to_str(z_sample_kind(sample)), (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); } void liveliness_handler(z_loaned_sample_t* sample, void* ctx) { (void)(ctx); z_view_string_t key_string; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &key_string); switch (z_sample_kind(sample)) { case Z_SAMPLE_KIND_PUT: printf(">> [Liveliness Subscriber] New alive token ('%.*s')\n", (int)z_string_len(z_loan(key_string)), z_string_data(z_loan(key_string))); break; case Z_SAMPLE_KIND_DELETE: printf(">> [Liveliness Subscriber] Dropped token ('%.*s')\n", (int)z_string_len(z_loan(key_string)), z_string_data(z_loan(key_string))); break; } } void miss_handler(const ze_miss_t* miss, void* arg) { (void)(arg); z_id_t id = z_entity_global_id_zid(&miss->source); z_owned_string_t id_string; z_id_to_string(&id, &id_string); printf(">> [Advanced Subscriber] Missed %d samples from '%.*s' !!!", miss->nb, (int)z_string_len(z_loan(id_string)), z_string_data(z_loan(id_string))); z_drop(z_move(id_string)); } int main(int argc, char** argv) { char* keyexpr = "demo/example/**"; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_history_options_default(&sub_opts.history); // or sub_opts.history.is_enabled = true; sub_opts.history.detect_late_publishers = true; ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); // or sub_opts.recovery.is_enabled = true; ze_advanced_subscriber_last_sample_miss_detection_options_default(&sub_opts.recovery.last_sample_miss_detection); // or sub_opts.recovery.last_sample_miss_detection.is_enabled = true; sub_opts.recovery.last_sample_miss_detection.periodic_queries_period_ms = 0; // use publisher heartbeats by default, otherwise enable periodic queries as follows: // sub_opts.recovery.last_sample_miss_detection.periodic_queries_period_ms = 1000; sub_opts.subscriber_detection = true; z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring AdvancedSubscriber on '%s'...\n", keyexpr); ze_owned_advanced_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (ze_declare_advanced_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), &sub_opts) < 0) { printf("Unable to declare advanced subscriber.\n"); return -1; } ze_owned_closure_miss_t miss_callback; ze_owned_sample_miss_listener_t miss_listener; z_closure(&miss_callback, miss_handler, NULL, NULL); ze_advanced_subscriber_declare_sample_miss_listener(z_loan(sub), &miss_listener, z_move(miss_callback)); z_owned_closure_sample_t liveliness_callback; z_closure(&liveliness_callback, liveliness_handler, NULL, NULL); z_liveliness_subscriber_options_t liveliness_sub_opt; z_liveliness_subscriber_options_default(&liveliness_sub_opt); z_owned_subscriber_t liveliness_sub; if (ze_advanced_subscriber_detect_publishers(z_loan(sub), &liveliness_sub, z_move(liveliness_callback), &liveliness_sub_opt) < 0) { printf("Unable to declare liveliness subscriber.\n"); exit(-1); } printf("Press CTRL-C to quit...\n"); while (1) { sleep(1); } // Clean up z_drop(z_move(miss_listener)); z_drop(z_move(liveliness_sub)); z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } const char* kind_to_str(z_sample_kind_t kind) { switch (kind) { case Z_SAMPLE_KIND_PUT: return "PUT"; case Z_SAMPLE_KIND_DELETE: return "DELETE"; default: return "UNKNOWN"; } } static int parse_args(int argc, char** argv, z_owned_config_t* config, char** ke) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_ADVANCED_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_bytes.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #undef NDEBUG #include typedef struct custom_struct_t { float f; uint64_t u[2][3]; const char *c; } custom_struct_t; typedef struct kv_pair_t { int32_t key; z_owned_string_t value; } kv_pair_t; static void print_slice_data(z_view_slice_t *slice); int main(void) { // Wrapping raw data into z_bytes_t z_owned_bytes_t payload; { const uint8_t input_bytes[] = {1, 2, 3, 4}; z_owned_slice_t output_bytes; z_bytes_copy_from_buf(&payload, input_bytes, sizeof(input_bytes)); z_bytes_to_slice(z_loan(payload), &output_bytes); assert(memcmp(input_bytes, z_slice_data(z_loan(output_bytes)), z_slice_len(z_loan(output_bytes))) == 0); z_drop(z_move(payload)); z_drop(z_move(output_bytes)); // Corresponding encoding to be used in operations options like `z_put()`, `z_get()`, etc. // const z_loaned_encoding* encoding = z_encoding_zenoh_bytes(); // The same can be done for const char* const char *input_str = "test"; z_owned_string_t output_string; z_bytes_copy_from_str(&payload, input_str); z_bytes_to_string(z_loan(payload), &output_string); assert(strncmp(input_str, z_string_data(z_loan(output_string)), z_string_len(z_loan(output_string))) == 0); z_drop(z_move(payload)); z_drop(z_move(output_string)); // Corresponding encoding to be used in operations options like `z_put()`, `z_get()`, etc. // const z_loaned_encoding* encoding = z_encoding_zenoh_string(); } // Serialization { // Arithmetic types: uint8, uint16, uint32, uint64, int8, int16, int32, int64, float, double uint32_t input_u32 = 1234; uint32_t output_u32 = 0; ze_serialize_uint32(&payload, input_u32); ze_deserialize_uint32(z_loan(payload), &output_u32); assert(input_u32 == output_u32); z_drop(z_move(payload)); // Corresponding encoding to be used in operations options like `z_put()`, `z_get()`, etc. // const z_loaned_encoding* encoding = z_encoding_zenoh_serialized(); } // Writer/reader for raw bytes { uint8_t input_writer[] = {0, 1, 2, 3, 4}; uint8_t output_reader[5] = {0}; z_owned_bytes_writer_t writer; z_bytes_writer_empty(&writer); z_bytes_writer_write_all(z_loan_mut(writer), input_writer, 3); z_bytes_writer_write_all(z_loan_mut(writer), input_writer + 3, 2); z_bytes_writer_finish(z_move(writer), &payload); z_bytes_reader_t reader = z_bytes_get_reader(z_loan(payload)); z_bytes_reader_read(&reader, output_reader, sizeof(output_reader)); assert(0 == memcmp(input_writer, output_reader, sizeof(output_reader))); z_drop(z_move(payload)); } // Using serializer/deserializer for composite types { // A sequence of primitive types int32_t input_vec[] = {1, 2, 3, 4}; int32_t output_vec[4] = {0}; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 4); for (size_t i = 0; i < 4; ++i) { ze_serializer_serialize_int32(z_loan_mut(serializer), input_vec[i]); } ze_serializer_finish(z_move(serializer), &payload); ze_deserializer_t deserializer = ze_deserializer_from_bytes(z_loan(payload)); size_t num_elements = 0; ze_deserializer_deserialize_sequence_length(&deserializer, &num_elements); assert(num_elements == 4); for (size_t i = 0; i < num_elements; ++i) { ze_deserializer_deserialize_int32(&deserializer, &output_vec[i]); } for (size_t i = 0; i < 4; ++i) { assert(input_vec[i] == output_vec[i]); } z_drop(z_move(payload)); } { // Sequence of key-value pairs kv_pair_t kvs_input[2]; kvs_input[0].key = 0; z_string_from_str(&kvs_input[0].value, "abc", NULL, NULL); kvs_input[1].key = 1; z_string_from_str(&kvs_input[1].value, "def", NULL, NULL); ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 2); for (size_t i = 0; i < 2; ++i) { ze_serializer_serialize_int32(z_loan_mut(serializer), kvs_input[i].key); ze_serializer_serialize_string(z_loan_mut(serializer), z_loan(kvs_input[i].value)); } ze_serializer_finish(z_move(serializer), &payload); ze_deserializer_t deserializer = ze_deserializer_from_bytes(z_loan(payload)); size_t num_elements = 0; ze_deserializer_deserialize_sequence_length(&deserializer, &num_elements); assert(num_elements == 2); kv_pair_t kvs_output[2]; for (size_t i = 0; i < num_elements; ++i) { ze_deserializer_deserialize_int32(&deserializer, &kvs_output[i].key); ze_deserializer_deserialize_string(&deserializer, &kvs_output[i].value); } for (size_t i = 0; i < 2; ++i) { assert(kvs_input[i].key == kvs_output[i].key); assert(strncmp(z_string_data(z_loan(kvs_input[i].value)), z_string_data(z_loan(kvs_output[i].value)), z_string_len(z_loan(kvs_input[i].value))) == 0); z_drop(z_move(kvs_input[i].value)); z_drop(z_move(kvs_output[i].value)); } z_drop(z_move(payload)); } { // Custom struct/tuple serializaiton custom_struct_t cs = (custom_struct_t){.f = 1.0f, .u = {{1, 2, 3}, {4, 5, 6}}, .c = "test"}; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_float(z_loan_mut(serializer), cs.f); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 2); for (size_t i = 0; i < 2; ++i) { ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 3); for (size_t j = 0; j < 3; ++j) { ze_serializer_serialize_uint64(z_loan_mut(serializer), cs.u[i][j]); } } ze_serializer_serialize_str(z_loan_mut(serializer), cs.c); ze_serializer_finish(z_move(serializer), &payload); float f = 0.0f; uint64_t u = 0; z_owned_string_t c; ze_deserializer_t deserializer = ze_deserializer_from_bytes(z_loan(payload)); ze_deserializer_deserialize_float(&deserializer, &f); assert(f == cs.f); size_t num_elements0 = 0; ze_deserializer_deserialize_sequence_length(&deserializer, &num_elements0); assert(num_elements0 == 2); for (size_t i = 0; i < 2; ++i) { size_t num_elements1 = 0; ze_deserializer_deserialize_sequence_length(&deserializer, &num_elements1); assert(num_elements1 == 3); for (size_t j = 0; j < 3; ++j) { ze_deserializer_deserialize_uint64(&deserializer, &u); assert(u == cs.u[i][j]); } } ze_deserializer_deserialize_string(&deserializer, &c); assert(strncmp(cs.c, z_string_data(z_loan(c)), z_string_len(z_loan(c))) == 0); z_drop(z_move(c)); z_drop(z_move(payload)); } // Slice iterator { /// Fill z_bytes with some data z_owned_bytes_t b1, b2, b3; z_bytes_copy_from_str(&b1, "abc"); z_bytes_copy_from_str(&b2, "def"); z_bytes_copy_from_str(&b3, "hij"); z_owned_bytes_writer_t writer; z_bytes_writer_empty(&writer); z_bytes_writer_append(z_loan_mut(writer), z_move(b1)); z_bytes_writer_append(z_loan_mut(writer), z_move(b2)); z_bytes_writer_append(z_loan_mut(writer), z_move(b3)); z_bytes_writer_finish(z_move(writer), &payload); z_bytes_slice_iterator_t slice_iter = z_bytes_get_slice_iterator(z_bytes_loan(&payload)); z_view_slice_t curr_slice; while (z_bytes_slice_iterator_next(&slice_iter, &curr_slice)) { printf("slice len: %d, slice data: '", (int)z_slice_len(z_view_slice_loan(&curr_slice))); print_slice_data(&curr_slice); printf("'\n"); } z_drop(z_move(payload)); } return 0; } static void print_slice_data(z_view_slice_t *slice) { for (size_t i = 0; i < z_slice_len(z_view_slice_loan(slice)); i++) { printf("0x%02x ", z_slice_data(z_view_slice_loan(slice))[i]); } } ================================================ FILE: examples/unix/c11/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 static z_owned_condvar_t cond; static z_owned_mutex_t mutex; const char *kind_to_str(z_sample_kind_t kind); static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value); void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); z_condvar_signal(z_loan_mut(cond)); z_drop(z_move(cond)); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received %s ('%.*s': '%.*s')\n", kind_to_str(z_sample_kind(sample)), (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { const z_loaned_reply_err_t *err = z_reply_err(reply); z_owned_string_t errstr; z_bytes_to_string(z_reply_err_payload(err), &errstr); printf(">> Received an error: %.*s\n", (int)z_string_len(z_loan(errstr)), z_string_data(z_loan(errstr))); z_drop(z_move(errstr)); } } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; char *value = NULL; z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_mutex_lock(z_loan_mut(mutex)); printf("Sending Query '%s'...\n", keyexpr); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (value != NULL) { z_bytes_from_static_str(&payload, value); opts.payload = z_bytes_move(&payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return -1; } z_condvar_wait(z_loan_mut(cond), z_loan_mut(mutex)); z_mutex_unlock(z_loan_mut(mutex)); z_drop(z_move(s)); return 0; } const char *kind_to_str(z_sample_kind_t kind) { switch (kind) { case Z_SAMPLE_KIND_PUT: return "PUT"; case Z_SAMPLE_KIND_DELETE: return "DELETE"; default: return "UNKNOWN"; } } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_get_attachment.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include typedef struct kv_pair_t { z_owned_string_t key; z_owned_string_t value; } kv_pair_t; #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 static z_owned_condvar_t cond; static z_owned_mutex_t mutex; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value); void print_attachment(const kv_pair_t *kvp, size_t len) { printf(" with attachment:\n"); for (size_t i = 0; i < len; i++) { printf(" %zu: %.*s, %.*s\n", i, (int)z_string_len(z_loan(kvp[i].key)), z_string_data(z_loan(kvp[i].key)), (int)z_string_len(z_loan(kvp[i].value)), z_string_data(z_loan(kvp[i].value))); } } void drop_attachment(kv_pair_t *kvp, size_t len) { for (size_t i = 0; i < len; i++) { z_drop(z_move(kvp[i].key)); z_drop(z_move(kvp[i].value)); } } void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); z_condvar_signal(z_loan_mut(cond)); z_drop(z_move(cond)); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); z_owned_string_t encoding; z_encoding_to_string(z_sample_encoding(sample), &encoding); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); printf(" with encoding: %.*s\n", (int)z_string_len(z_loan(encoding)), z_string_data(z_loan(encoding))); z_drop(z_move(replystr)); z_drop(z_move(encoding)); // Check attachment const z_loaned_bytes_t *attachment = z_sample_attachment(sample); ze_deserializer_t deserializer = ze_deserializer_from_bytes(attachment); size_t attachment_len; if (ze_deserializer_deserialize_sequence_length(&deserializer, &attachment_len) < 0) { return; } kv_pair_t *kvp = (kv_pair_t *)malloc(sizeof(kv_pair_t) * attachment_len); for (size_t i = 0; i < attachment_len; ++i) { ze_deserializer_deserialize_string(&deserializer, &kvp[i].key); ze_deserializer_deserialize_string(&deserializer, &kvp[i].value); } if (attachment_len > 0) { print_attachment(kvp, attachment_len); } drop_attachment(kvp, attachment_len); free(kvp); } else { printf(">> Received an error\n"); } } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; char *value = NULL; z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_mutex_lock(z_loan_mut(mutex)); printf("Sending Query '%s'...\n", keyexpr); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (value != NULL) { z_bytes_from_static_str(&payload, value); opts.payload = z_bytes_move(&payload); } // Add attachment value kv_pair_t kvs[1]; z_string_from_str(&kvs[0].key, "test_key", NULL, NULL); z_string_from_str(&kvs[0].value, "test_value", NULL, NULL); z_owned_bytes_t attachment; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 1); for (size_t i = 0; i < 1; ++i) { ze_serializer_serialize_string(z_loan_mut(serializer), z_loan(kvs[i].key)); ze_serializer_serialize_string(z_loan_mut(serializer), z_loan(kvs[i].value)); } drop_attachment(kvs, 1); ze_serializer_finish(z_move(serializer), &attachment); opts.attachment = z_move(attachment); // Add encoding value z_owned_encoding_t encoding; z_encoding_from_str(&encoding, "zenoh/string;utf8"); opts.encoding = z_move(encoding); z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return -1; } z_condvar_wait(z_loan_mut(cond), z_loan_mut(mutex)); z_mutex_unlock(z_loan_mut(mutex)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_get_channel.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value); int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; char *value = NULL; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Sending Query '%s'...\n", keyexpr); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (value != NULL) { z_bytes_from_static_str(&payload, value); opts.payload = z_bytes_move(&payload); } z_owned_closure_reply_t closure; z_owned_ring_handler_reply_t handler; z_ring_channel_reply_new(&closure, &handler, 1); if (z_get(z_loan(s), z_loan(ke), "", z_move(closure), &opts) < 0) { printf("Unable to send query.\n"); return -1; } z_owned_reply_t reply; for (z_result_t res = z_recv(z_loan(handler), &reply); res == Z_OK; res = z_recv(z_loan(handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(">> Received an error\n"); } } z_drop(z_move(handler)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_get_lat.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 && defined Z_FEATURE_UNSTABLE_API #define DEFAULT_PKT_SIZE 8 #define DEFAULT_PING_NB 100 #define DEFAULT_WARMUP_MS 1000 static int parse_args(int argc, char **argv, z_owned_config_t *config, unsigned int *size, unsigned int *ping_nb, unsigned int *warmup_ms, bool *is_peer); static _Atomic unsigned long sync_tx_rx = 0; static unsigned long load_loop(unsigned long target_value) { unsigned long curr_val; do { curr_val = atomic_load_explicit(&sync_tx_rx, memory_order_relaxed); } while (curr_val != target_value); return curr_val; } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)reply; (void)ctx; atomic_fetch_add_explicit(&sync_tx_rx, 1, memory_order_relaxed); } int main(int argc, char **argv) { unsigned int pkt_size = DEFAULT_PKT_SIZE; unsigned int ping_nb = DEFAULT_PING_NB; unsigned int warmup_ms = DEFAULT_WARMUP_MS; bool is_peer = false; // Set config z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &pkt_size, &ping_nb, &warmup_ms, &is_peer); if (ret != 0) { return ret; } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Wait for queryable to stabilize z_sleep_ms(1); // Get keyexpr & closure z_view_keyexpr_t ke; z_owned_querier_t que; z_view_keyexpr_from_str_unchecked(&ke, "lat"); if (z_declare_querier(z_loan(s), &que, z_loan(ke), NULL) < 0) { printf("Unable to declare querier for key expression!\n"); return -1; } unsigned long prev_val = sync_tx_rx; z_owned_closure_reply_t callback; // Send packets if (warmup_ms) { z_clock_t warmup_start = z_clock_now(); unsigned long elapsed_us = 0; while (elapsed_us < warmup_ms * 1000) { z_closure(&callback, reply_handler, NULL, NULL); if (z_querier_get(z_loan(que), NULL, z_move(callback), NULL) < 0) { printf("Tx failed"); continue; } prev_val = load_loop(prev_val + 1); elapsed_us = z_clock_elapsed_us(&warmup_start); } } unsigned long *results = (unsigned long *)z_malloc(sizeof(unsigned long) * ping_nb); for (unsigned int i = 0; i < ping_nb; i++) { z_clock_t measure_start = z_clock_now(); z_closure(&callback, reply_handler, NULL, NULL); if (z_querier_get(z_loan(que), NULL, z_move(callback), NULL) < 0) { printf("Tx failed"); continue; } prev_val = load_loop(prev_val + 1); results[i] = z_clock_elapsed_us(&measure_start); } for (unsigned int i = 0; i < ping_nb; i++) { printf("%lu\n", results[i]); } // Clean up z_free(results); z_drop(z_move(que)); z_drop(z_move(s)); exit(0); } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, unsigned int *size, unsigned int *ping_nb, unsigned int *warmup_ms, bool *is_peer) { int opt; while ((opt = getopt(argc, argv, "s:n:w:e:m:l:")) != -1) { switch (opt) { case 's': *size = (unsigned int)atoi(optarg); break; case 'n': *ping_nb = (unsigned int)atoi(optarg); break; case 'w': *warmup_ms = (unsigned int)atoi(optarg); break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); if (strcmp(optarg, "peer") == 0) { *is_peer = true; } break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 's' || optopt == 'n' || optopt == 'w' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY, Z_FEATURE_MULTI_THREAD or Z_FEATURE_UNSTABLE_API but " "this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_get_liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_LIVELINESS == 1 && Z_FEATURE_QUERY == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr); int main(int argc, char **argv) { char *keyexpr = "group1/**"; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Sending liveliness query '%s'...\n", keyexpr); z_owned_fifo_handler_reply_t handler; z_owned_closure_reply_t closure; z_fifo_channel_reply_new(&closure, &handler, 16); if (z_liveliness_get(z_loan(s), z_loan(ke), z_move(closure), NULL) < 0) { printf("Liveliness query failed"); return -1; } z_owned_reply_t reply; for (z_result_t res = z_recv(z_loan(handler), &reply); res == Z_OK; res = z_recv(z_loan(handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t key_str; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &key_str); printf(">> Alive token ('%.*s')\n", (int)z_string_len(z_loan(key_str)), z_string_data(z_loan(key_str))); } else { printf("Received an error\n"); } z_drop(z_move(reply)); } z_drop(z_move(closure)); z_drop(z_move(handler)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_LIVELINESS but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_info.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include void print_zid(const z_id_t *id, void *ctx) { (void)(ctx); z_owned_string_t id_str; z_id_to_string(id, &id_str); printf(" %.*s\n", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str))); z_drop(z_move(id_str)); } #if Z_FEATURE_CONNECTIVITY == 1 static const char *bool_to_str(bool value) { return value ? "true" : "false"; } static void print_transport(z_loaned_transport_t *transport, void *ctx) { (void)(ctx); z_id_t zid = z_transport_zid(transport); z_owned_string_t id_str; z_id_to_string(&zid, &id_str); z_view_string_t whatami; z_whatami_to_view_string(z_transport_whatami(transport), &whatami); printf(" transport{zid=%.*s, whatami=%.*s, is_qos=%s, is_multicast=%s, is_shm=%s}\n", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str)), (int)z_string_len(z_loan(whatami)), z_string_data(z_loan(whatami)), bool_to_str(z_transport_is_qos(transport)), bool_to_str(z_transport_is_multicast(transport)), bool_to_str(z_transport_is_shm(transport))); z_drop(z_move(id_str)); } static void print_link(z_loaned_link_t *link, void *ctx) { (void)(ctx); z_id_t zid = z_link_zid(link); z_owned_string_t id_str; z_id_to_string(&zid, &id_str); z_owned_string_t src; z_owned_string_t dst; bool has_src = z_link_src(link, &src) == 0; bool has_dst = z_link_dst(link, &dst) == 0; printf(" link{zid=%.*s", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str))); printf(", src="); if (has_src) { printf("%.*s", (int)z_string_len(z_loan(src)), z_string_data(z_loan(src))); } else { printf(""); } printf(", dst="); if (has_dst) { printf("%.*s", (int)z_string_len(z_loan(dst)), z_string_data(z_loan(dst))); } else { printf(""); } z_owned_string_t group; z_link_group(link, &group); z_owned_string_t auth_id; z_link_auth_identifier(link, &auth_id); z_owned_string_array_t interfaces; z_link_interfaces(link, &interfaces); uint8_t prio_min, prio_max; bool has_prio = z_link_priorities(link, &prio_min, &prio_max); z_reliability_t reliability; bool has_rel = z_link_reliability(link, &reliability); if (z_string_len(z_loan(group)) > 0) { printf(", group=%.*s", (int)z_string_len(z_loan(group)), z_string_data(z_loan(group))); } if (z_string_len(z_loan(auth_id)) > 0) { printf(", auth_id=%.*s", (int)z_string_len(z_loan(auth_id)), z_string_data(z_loan(auth_id))); } printf(", interfaces=["); size_t iface_len = z_string_array_len(z_loan(interfaces)); for (size_t i = 0; i < iface_len; i++) { const z_loaned_string_t *iface = z_string_array_get(z_loan(interfaces), i); if (i > 0) printf(", "); printf("%.*s", (int)z_string_len(iface), z_string_data(iface)); } printf("]"); if (has_prio) { printf(", priorities=[%u, %u]", (unsigned)prio_min, (unsigned)prio_max); } if (has_rel) { printf(", reliability=%s", (reliability == Z_RELIABILITY_RELIABLE) ? "reliable" : "best_effort"); } printf(", mtu=%u, is_streamed=%s, is_reliable=%s}\n", (unsigned)z_link_mtu(link), bool_to_str(z_link_is_streamed(link)), bool_to_str(z_link_is_reliable(link))); z_drop(z_move(id_str)); if (has_src) { z_drop(z_move(src)); } if (has_dst) { z_drop(z_move(dst)); } z_drop(z_move(group)); z_drop(z_move(auth_id)); z_drop(z_move(interfaces)); } static volatile bool running = true; static void handle_signal(int signo) { (void)signo; running = false; } static void transport_event_handler(z_loaned_transport_event_t *event, void *ctx) { (void)ctx; z_sample_kind_t kind = z_transport_event_kind(event); const z_loaned_transport_t *transport = z_transport_event_transport(event); printf(">> [Transport Event] %s:\n", (kind == Z_SAMPLE_KIND_PUT) ? "Opened" : "Closed"); print_transport((z_loaned_transport_t *)transport, NULL); } static void link_event_handler(z_loaned_link_event_t *event, void *ctx) { (void)ctx; z_sample_kind_t kind = z_link_event_kind(event); const z_loaned_link_t *link = z_link_event_link(event); printf(">> [Link Event] %s:\n", (kind == Z_SAMPLE_KIND_PUT) ? "Opened" : "Closed"); print_link((z_loaned_link_t *)link, NULL); } #endif static int parse_args(int argc, char **argv, z_owned_config_t *config); int main(int argc, char **argv) { z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_id_t self_id = z_info_zid(z_loan(s)); printf("Own ID:"); print_zid(&self_id, NULL); printf("Routers IDs:\n"); z_owned_closure_zid_t callback; z_closure(&callback, print_zid, NULL, NULL); z_info_routers_zid(z_loan(s), z_move(callback)); // `callback` has been `z_move`d just above, so it's safe to reuse the variable, // we'll just have to make sure we `z_move` it again to avoid mem-leaks. printf("Peers IDs:\n"); z_owned_closure_zid_t callback2; z_closure(&callback2, print_zid, NULL, NULL); z_info_peers_zid(z_loan(s), z_move(callback2)); #if Z_FEATURE_CONNECTIVITY == 1 printf("Connected transports:\n"); z_owned_closure_transport_t transport_cb; z_closure(&transport_cb, print_transport, NULL, NULL); if (z_info_transports(z_loan(s), z_move(transport_cb)) < 0) { printf("Unable to fetch connected transports\n"); z_drop(z_move(s)); return -1; } printf("Connected links:\n"); z_owned_closure_link_t link_cb; z_closure(&link_cb, print_link, NULL, NULL); if (z_info_links(z_loan(s), z_move(link_cb), NULL) < 0) { printf("Unable to fetch connected links\n"); z_drop(z_move(s)); return -1; } // Register transport event listener (background, no history to avoid duplicating already-printed items) printf("Declaring transport events listener...\n"); z_owned_closure_transport_event_t te_cb; z_closure(&te_cb, transport_event_handler, NULL, NULL); z_transport_events_listener_options_t te_opts; z_transport_events_listener_options_default(&te_opts); te_opts.history = false; if (z_declare_background_transport_events_listener(z_loan(s), z_move(te_cb), &te_opts) < 0) { printf("Unable to declare transport events listener\n"); z_drop(z_move(s)); return -1; } // Register link event listener (non-background so we can undeclare it) printf("Declaring link events listener...\n"); z_owned_closure_link_event_t le_cb; z_closure(&le_cb, link_event_handler, NULL, NULL); z_link_events_listener_options_t le_opts; z_link_events_listener_options_default(&le_opts); le_opts.history = false; z_owned_link_events_listener_t le_listener; if (z_declare_link_events_listener(z_loan(s), &le_listener, z_move(le_cb), &le_opts) < 0) { printf("Unable to declare link events listener\n"); z_drop(z_move(s)); return -1; } signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); printf("Press CTRL-C to quit...\n"); while (running) { z_sleep_s(1); } z_drop(z_move(le_listener)); #endif z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config) { int opt; while ((opt = getopt(argc, argv, "e:m:l:")) != -1) { switch (opt) { case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } ================================================ FILE: examples/unix/c11/z_liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_LIVELINESS == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, unsigned long *timeout); int main(int argc, char **argv) { char *keyexpr = "group1/zenoh-pico"; unsigned long timeout = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &timeout); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Declaring liveliness token '%s'...\n", keyexpr); z_owned_liveliness_token_t token; if (z_liveliness_declare_token(z_loan(s), &token, z_loan(ke), NULL) < 0) { printf("Unable to create liveliness token!\n"); exit(-1); } printf("Press CTRL-C to undeclare liveliness token and quit...\n"); z_clock_t clock = z_clock_now(); while (timeout == 0 || z_clock_elapsed_s(&clock) < timeout) { z_sleep_ms(100); } // LivelinessTokens are automatically closed when dropped // Use the code below to manually undeclare it if needed printf("Undeclaring liveliness token...\n"); z_drop(z_move(token)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, unsigned long *timeout) { int opt; while ((opt = getopt(argc, argv, "k:t:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 't': *timeout = (unsigned long)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 't' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_ping.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/system/platform.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 1 #define DEFAULT_PKT_SIZE 8 #define DEFAULT_PING_NB 100 #define DEFAULT_WARMUP_MS 1000 static int parse_args(int argc, char** argv, z_owned_config_t* config, unsigned int* size, unsigned int* ping_nb, unsigned int* warmup_ms, bool* is_peer); static _Atomic unsigned long sync_tx_rx = 0; void callback(z_loaned_sample_t* sample, void* context) { (void)sample; (void)context; atomic_fetch_add_explicit(&sync_tx_rx, 1, memory_order_relaxed); } static unsigned long load_loop(unsigned long target_value) { unsigned long curr_val; do { curr_val = atomic_load_explicit(&sync_tx_rx, memory_order_relaxed); } while (curr_val != target_value); return curr_val; } int main(int argc, char** argv) { unsigned int pkt_size = DEFAULT_PKT_SIZE; unsigned int ping_nb = DEFAULT_PING_NB; unsigned int warmup_ms = DEFAULT_WARMUP_MS; bool is_peer = false; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &pkt_size, &ping_nb, &warmup_ms, &is_peer); if (ret != 0) { return ret; } z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(session), &pub, z_loan(ping), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_owned_closure_sample_t respond; z_closure(&respond, callback); if (z_declare_background_subscriber(z_loan(session), z_loan(pong), z_move(respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } uint8_t* data = (uint8_t*)z_malloc(pkt_size); for (unsigned int i = 0; i < pkt_size; i++) { data[i] = (uint8_t)(i % 10); } // Wait for declare to be processed z_sleep_ms(50); // Create payload unsigned long prev_val = sync_tx_rx; z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, pkt_size, NULL, NULL); z_owned_bytes_t curr_payload; if (warmup_ms) { z_clock_t warmup_start = z_clock_now(); unsigned long elapsed_us = 0; while (elapsed_us < warmup_ms * 1000) { z_bytes_clone(&curr_payload, z_loan(payload)); if (z_publisher_put(z_loan(pub), z_move(curr_payload), NULL) != _Z_RES_OK) { printf("Tx failed"); continue; } prev_val = load_loop(prev_val + 1); elapsed_us = z_clock_elapsed_us(&warmup_start); } } unsigned long* results = (unsigned long*)z_malloc(sizeof(unsigned long) * ping_nb); for (unsigned int i = 0; i < ping_nb; i++) { z_clock_t measure_start = z_clock_now(); z_bytes_clone(&curr_payload, z_loan(payload)); if (z_publisher_put(z_loan(pub), z_move(curr_payload), NULL) != _Z_RES_OK) { printf("Tx failed"); continue; } prev_val = load_loop(prev_val + 1); results[i] = z_clock_elapsed_us(&measure_start); } for (unsigned int i = 0; i < ping_nb; i++) { printf("%lu\n", results[i]); } z_drop(z_move(payload)); z_free(results); z_free(data); z_drop(z_move(pub)); z_drop(z_move(session)); } static int parse_args(int argc, char** argv, z_owned_config_t* config, unsigned int* size, unsigned int* ping_nb, unsigned int* warmup_ms, bool* is_peer) { int opt; while ((opt = getopt(argc, argv, "s:n:w:e:m:l:")) != -1) { switch (opt) { case 's': *size = (unsigned int)atoi(optarg); break; case 'n': *ping_nb = (unsigned int)atoi(optarg); break; case 'w': *warmup_ms = (unsigned int)atoi(optarg); break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); if (strcmp(optarg, "peer") == 0) { *is_peer = true; } break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 's' || optopt == 'n' || optopt == 'w' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION or " "Z_FEATURE_MULTI_THREAD but this example requires them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pong.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 static int parse_args(int argc, char** argv, z_owned_config_t* config); void callback(z_loaned_sample_t* sample, void* context) { const z_loaned_publisher_t* pub = z_loan(*(z_owned_publisher_t*)context); z_owned_bytes_t payload = {._val = sample->payload}; z_publisher_put(pub, z_move(payload), NULL); } int main(int argc, char** argv) { (void)argc; (void)argv; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config); if (ret != 0) { return ret; } z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(session), &pub, z_loan(pong), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_owned_closure_sample_t respond; z_closure(&respond, callback, NULL, (void*)(&pub)); if (z_declare_background_subscriber(z_loan(session), z_loan(ping), z_move(respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } printf("Pong app is running, press 'q' to quit\n"); while (getchar() != 'q') { } z_drop(z_move(pub)); z_drop(z_move(session)); } static int parse_args(int argc, char** argv, z_owned_config_t* config) { int opt; while ((opt = getopt(argc, argv, "e:m:l:")) != -1) { switch (opt) { case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION but this example " "requires them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n, bool *add_matching_listener); #if Z_FEATURE_MATCHING == 1 void matching_status_handler(const z_matching_status_t *matching_status, void *arg) { (void)arg; if (matching_status->matching) { printf("Publisher has matching subscribers.\n"); } else { printf("Publisher has NO MORE matching subscribers.\n"); } } #endif int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-pub"; char *const default_value = "Pub from Pico!"; char *value = default_value; int n = 2147483647; // max int value by default bool add_matching_listener = false; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &n, &add_matching_listener); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif // Declare publisher printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } if (add_matching_listener) { #if Z_FEATURE_MATCHING == 1 z_owned_closure_matching_status_t callback; z_closure(&callback, matching_status_handler, NULL, NULL); z_publisher_declare_background_matching_listener(z_loan(pub), z_move(callback)); #else printf("ERROR: Zenoh pico was compiled without Z_FEATURE_MATCHING but this example requires it.\n"); return -2; #endif } // Publish data printf("Press CTRL-C to quit...\n"); char buf[256]; for (int idx = 0; idx < n; ++idx) { z_sleep_s(1); sprintf(buf, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); } // Clean up z_drop(z_move(pub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n, bool *add_matching_listener) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:a")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case 'a': *add_matching_listener = true; break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pub_attachment.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include #include typedef struct kv_pair_t { const char *key; const char *value; } kv_pair_t; #if Z_FEATURE_PUBLICATION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n); int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-pub"; char *const default_value = "Pub from Pico!"; char *value = default_value; int n = 2147483647; // max int value by default z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif // Declare publisher printf("Declaring publisher for '%s'...\n", keyexpr); z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_owned_publisher_t pub; if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_publisher_put_options_t options; z_publisher_put_options_default(&options); // Allocate attachment kv_pair_t kvs[2]; kvs[0] = (kv_pair_t){.key = "source", .value = "C"}; z_owned_bytes_t attachment; z_bytes_empty(&attachment); // Create encoding z_owned_encoding_t encoding; // Create timestamp z_timestamp_t ts; z_timestamp_new(&ts, z_loan(s)); // Publish data printf("Press CTRL-C to quit...\n"); char buf[256]; for (int idx = 0; idx < n; ++idx) { z_sleep_s(1); sprintf(buf, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); // Add attachment value char buf_ind[16]; sprintf(buf_ind, "%d", idx); kvs[1] = (kv_pair_t){.key = "index", .value = buf_ind}; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 2); for (size_t i = 0; i < 2; ++i) { ze_serializer_serialize_str(z_loan_mut(serializer), kvs[i].key); ze_serializer_serialize_str(z_loan_mut(serializer), kvs[i].value); } ze_serializer_finish(z_move(serializer), &attachment); options.attachment = z_move(attachment); // Add encoding value z_encoding_from_str(&encoding, "zenoh/string;utf8"); options.encoding = z_move(encoding); // Add timestamp options.timestamp = &ts; z_publisher_put(z_loan(pub), z_move(payload), &options); } // Clean up z_drop(z_move(pub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 0 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n); int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-pub"; char *const default_value = "Pub from Pico!"; char *value = default_value; int n = 2147483647; // max int value by default z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } // Main loop printf("Press CTRL-C to quit...\n"); char buf[256]; z_clock_t now = z_clock_now(); for (int idx = 0; idx < n;) { if (z_clock_elapsed_ms(&now) > 1000) { snprintf(buf, 256, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); ++idx; now = z_clock_now(); } z_sleep_ms(50); zp_spin_once(z_session_loan(&s)); } z_drop(z_move(pub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value, int *n) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_PUBLICATION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pub_thr.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/protocol/codec.h" #define ZENOH_MSH_HDR_SIZE 3 #define ZENOH_FRAME_HDR_SIZE 2 #define ZENOH_TCP_MTU 49150 #define ZENOH_UDP_MTU 1450 #define ZENOH_TCP_PEER_MTU 65533 #define TEST_RUN_TIME_S 20 typedef struct { unsigned long frequency; _Atomic unsigned long count; bool *stop_flag; } z_stats_t; #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, unsigned long *freq, size_t *pkt_size, bool *is_peer, bool *is_udp); void z_free_with_context(void *ptr, void *context) { (void)context; z_free(ptr); } unsigned long cas_loop(z_stats_t *ctx, unsigned long value) { unsigned long prev_val = atomic_load_explicit(&ctx->count, memory_order_relaxed); unsigned long curr_val = prev_val; while (curr_val != value) { prev_val = curr_val; if (atomic_compare_exchange_weak_explicit(&ctx->count, &curr_val, value, memory_order_acquire, memory_order_relaxed)) { return prev_val; } } return prev_val; } void *measure_task(void *ctx) { z_stats_t *stats = (z_stats_t *)ctx; unsigned long sleep_dur_us = 1000000 / stats->frequency; while (!*stats->stop_flag) { z_clock_t start = z_clock_now(); z_sleep_us(sleep_dur_us); unsigned long elapsed = z_clock_elapsed_us(&start); // Clear count unsigned long count = cas_loop(stats, 0); if (count > 0) { printf("%.3lf\n", (double)count * 1000000.0 / (double)elapsed); } } return NULL; } void *stop_task(void *ctx) { z_sleep_s(TEST_RUN_TIME_S); bool *stop_flag = (bool *)ctx; *stop_flag = true; return NULL; } int main(int argc, char **argv) { char *keyexpr = "thr"; size_t len = 8; bool is_peer = false; bool is_udp = false; z_stats_t *context = malloc(sizeof(z_stats_t)); atomic_store_explicit(&context->count, 0, memory_order_relaxed); context->frequency = 10; // Calculate max mtu batch size size_t batch_size = 0; #if Z_FEATURE_BATCHING == 1 uint_fast8_t vle_size = _z_zint_len(len); uint_fast8_t vle_str = _z_zint_len(strlen(keyexpr)); size_t msg_size = 0; size_t max_mtu_size = 0; size_t zenoh_overhead = 0; if (is_peer) { if ((is_udp)) { zenoh_overhead = vle_size + vle_str + strlen(keyexpr) + ZENOH_MSH_HDR_SIZE; msg_size = len + zenoh_overhead; max_mtu_size = (ZENOH_UDP_MTU - ZENOH_FRAME_HDR_SIZE); } else { zenoh_overhead = vle_size + ZENOH_MSH_HDR_SIZE; msg_size = len + zenoh_overhead; max_mtu_size = (ZENOH_TCP_PEER_MTU - ZENOH_FRAME_HDR_SIZE); } batch_size = max_mtu_size / msg_size; } if (!is_peer) { zenoh_overhead = vle_size + ZENOH_MSH_HDR_SIZE; msg_size = len + zenoh_overhead; max_mtu_size = (ZENOH_TCP_MTU - ZENOH_FRAME_HDR_SIZE); batch_size = max_mtu_size / msg_size; } if (batch_size < 2) { batch_size = 0; } // double overhead = (double)(ZENOH_FRAME_HDR_SIZE) / (ZENOH_FRAME_HDR_SIZE + (double)(msg_size * batch_size)) + // (double)(zenoh_overhead); #endif // Set config z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &context->frequency, &len, &is_peer, &is_udp); if (ret != 0) { return ret; } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Timer for sub to prepare z_sleep_s(1); // Declare publisher z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_publisher_options_t opts; z_publisher_options_default(&opts); opts.congestion_control = Z_CONGESTION_CONTROL_DROP; if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), &opts) < 0) { printf("Unable to declare publisher for key expression!\n"); exit(-1); } uint8_t *value = (uint8_t *)z_malloc(len); memset(value, 1, len); z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, len, z_free_with_context, NULL); bool stop_flag = false; context->stop_flag = &stop_flag; pthread_t task; pthread_create(&task, NULL, measure_task, (void *)context); pthread_t task2; pthread_create(&task2, NULL, stop_task, &stop_flag); // Send packets (void)batch_size; #if Z_FEATURE_BATCHING == 1 if (batch_size > 0) zp_batch_start(z_loan(s)); #endif z_owned_bytes_t p; while (!stop_flag) { // Clone payload z_bytes_clone(&p, z_loan(payload)); z_publisher_put(z_loan(pub), z_move(p), NULL); atomic_fetch_add_explicit(&context->count, 1, memory_order_relaxed); } #if Z_FEATURE_BATCHING == 1 if (batch_size > 0) zp_batch_stop(z_loan(s)); #endif // Clean up pthread_join(task, NULL); pthread_join(task2, NULL); z_drop(z_move(pub)); z_drop(z_move(s)); z_drop(z_move(payload)); z_free(context); exit(0); } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, unsigned long *freq, size_t *pkt_size, bool *is_peer, bool *is_udp) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:f:s:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); if (strncmp(optarg, "udp", 3) == 0) { *is_udp = true; } break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); if (strcmp(optarg, "peer") == 0) { *is_peer = true; } break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); if (strncmp(optarg, "udp", 3) == 0) { *is_udp = true; } break; case 'f': *freq = (unsigned long)atoi(optarg); break; case 's': *pkt_size = (size_t)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'f' || optopt == 's') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_pub_tls.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_LINK_TLS == 1 static const char SERVER_CA_PEM[] = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURTekNDQWpPZ0F3SUJBZ0lJVGN3djFOMTBu" "cUV3RFFZSktvWklodmNOQVFFTEJRQXdJREVlTUJ3R0ExVUUKQXhNVmJXbHVhV05oSUhKdmIzUWdZ" "MkVnTkdSall6Sm1NQ0FYRFRJek1ETXdOakUyTkRFd05sb1lEekl4TWpNdwpNekEyTVRZME1UQTJX" "akFnTVI0d0hBWURWUVFERXhWdGFXNXBZMkVnY205dmRDQmpZU0EwWkdOak1tWXdnZ0VpCk1BMEdD" "U3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzJXVWdON05NbFhJa25ldzFjWGlUV0dt" "UzAKMVQxRWpjTk5EQXE3RHFaNy9aVlhyakQ0N3l4VHQ1RU9pT1hLL2NJTktOdzRacS9NS1F2cTlx" "dStPYXg0bHdpVgpIYTBpOFNoR0xTdVlJMUhCbFh1NE1tdmRHKzMvU2p3WW9Hc0dhU2hyMHkvUUd6" "RDNjRCtEUVpnL1JhYUlQSGxPCk1kbWlVWHhrTWN5NHFhMGhGSjFpbWxKZHEvNlRseDQ2WCswdlJD" "aDhua2Vrdk9aUit0N1o1VTRqbjRYRTU0S2wKMFBpd2N5WDh2ZkRaM2VwYS9GU0hadlZRaWVNL2c1" "WWg5T2pJS0NrZFdSZzd0RDBJRUdzYVcxMXRFUEo1U2lRcgptRHFkUm5lTXpaS3FZMHhDK1FxWFN2" "SWx6cE9qaXU4UFlReDd4dWdhVUZFL25wS1JRZHZoOG9qSEpNZE5BZ01CCkFBR2pnWVl3Z1lNd0Rn" "WURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQkJnZ3IKQmdFRkJR" "Y0RBakFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWREZ1FXQkJUWDQ2K3ArUG8xbnBF" "NgpRTFE3bU1JKzgzczZxREFmQmdOVkhTTUVHREFXZ0JUWDQ2K3ArUG8xbnBFNlFMUTdtTUkrODNz" "NnFEQU5CZ2txCmhraUc5dzBCQVFzRkFBT0NBUUVBYU4wSXZFQzY3N1BML0pYek1yWGN5QlY4OEl2" "aW1sWU4wekN0NDhHWWxobXgKdkwxWVVERkxKRUI3SitkeUVSR0U1TjZCS0tER2JsQzRXaVRGZ0RN" "TGNIRnNNR1JjMHY3ektQRjFQU0J3UllKaQp1YkFta3dkdW5HRzVwRFBVWXRURURQWE1sZ0NsWjBZ" "eXFTRkpNT3FBNEl6UWc2ZXhWalh0VXhQcXp4Tmh5QzdTCnZsZ1V3UGJYNDZ1Tmk1ODFhOStMczJW" "M2ZnMFpuaGtUU2N0WVpIR1pOZWgwTnNmN0FtOHhkVURZRy9iWmNWZWYKamJROWdwQ2hvc2RqRjBC" "Z2JsbzdIU1VjdC8yVmErWWxZd1crV0ZqSlg4azRvTjZaVTVXNXhoZGZPOEN6bWd3awpVUzVrSi8r" "MU0wdVI4elVoWkhMNjFGYnNkUHhFaitmWUtySHY0d29vK0E9PQotLS0tLUVORCBDRVJUSUZJQ0FU" "RS0tLS0tCg=="; static const char SERVER_CERT_PEM[] = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhhZ0F3SUJBZ0lJVzFtQXRKV0pB" "Sll3RFFZSktvWklodmNOQVFFTEJRQXdJREVlTUJ3R0ExVUUKQXhNVmJXbHVhV05oSUhKdmIzUWdZ" "MkVnTkdSall6Sm1NQ0FYRFRJek1ETXdOakUyTkRFd05sb1lEekl4TWpNdwpNekEyTVRZME1UQTJX" "akFVTVJJd0VBWURWUVFERXdsc2IyTmhiR2h2YzNRd2dnRWlNQTBHQ1NxR1NJYjNEUUVCCkFRVUFB" "NElCRHdBd2dnRUtBb0lCQVFDWU1MSktvb2MrWVJsS0VNZmVWMDlwWDlteUgzNGVVY1V1VDBmWFM4" "bG0KUGxaL05XN21tNWxEd2E4RVVnNjFXdVhRdjJvdVFEcHRtSWNkZWIvdzRSVzkzWGZsa3luZzFY" "YmQ5MU93UUJKZAorOFpWQmp6TDdoU1JrM1FQRHF4L0NWQlUvSTFHbVhLemI2Y1d6cTFmVGtPbjFX" "TE5YZjIxSTZwNytOM3FITFBGCkpRZW9WcTFIQkJGY0FqVGdKbnB5UU52UkdMRHVMVEsrT3NXRUdp" "YjJVOHFyZ2lSZGthQkxreEdYU2xHQUJsT28KY1F5Vy96T2hmNHB3YjJaL0pBZ2UybVJXNUljZXhD" "UEJXaW50OHlkUHNvSkRkczhqNStBeVlDRDZIVWhIWDBPYgpRa3o3M09XN2YyUFFodVRLMnV6S3kw" "WXo2bE5GdDJudXphV0MwNHdJVzNUN0FnTUJBQUdqZGpCME1BNEdBMVVkCkR3RUIvd1FFQXdJRm9E" "QWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3REFZRFZSMFQKQVFIL0JB" "SXdBREFmQmdOVkhTTUVHREFXZ0JUWDQ2K3ArUG8xbnBFNlFMUTdtTUkrODNzNnFEQVVCZ05WSFJF" "RQpEVEFMZ2dsc2IyTmhiR2h2YzNRd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBeHJtUVBHNTR5" "YktnTVZsaU44Ck1nNXBvdlNkUElWVm5sVS9IT1ZHOXl4ekFPYXYveFFQMDAzTTR3cXBhdFd4STh0" "UjFQY0x1WmYwRVBtY2RKZ2IKdFZsOW5aTVZadHZlUW5ZTWxVOFBwa0VWdTU2Vk00WnIzckg5bGlQ" "UmxyMEpFQVhPRGRLdzc2a1dLem1kcVdaLwpyemh1cDNFazdpRVg2VDVqL2NQVXZUV3RNRDRWRUsy" "STdmZ29LU0hJWDhNSVZ6cU03Y3Vib0dXUHRTM2VSTlhsCk1ndmFoQTRUd0xFWFBFZStWMVdBcTZu" "U2I0ZzJxU1hXSURwSXN5L08xV0dTL3p6Um5Ldlh1OS85TmtYV3FaTWwKQzFMU3BpaVFVYVJTZ2xP" "dllmL1p4NnIrNEJPUzRPYWFBcndIa2VjWlFxQlNDY0JMRUF5Yi9GYWFYZEJvd0kwVQpQUTQ9Ci0t" "LS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"; static const char SERVER_KEY_PEM[] = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbURDeVNxS0hQ" "bUVaU2hESDNsZFBhVi9ac2g5K0hsSEZMazlIMTB2SlpqNVdmelZ1CjVwdVpROEd2QkZJT3RWcmww" "TDlxTGtBNmJaaUhIWG0vOE9FVnZkMTM1Wk1wNE5WMjNmZFRzRUFTWGZ2R1ZRWTgKeSs0VWtaTjBE" "dzZzZndsUVZQeU5ScGx5czIrbkZzNnRYMDVEcDlWaXpWMzl0U09xZS9qZDZoeXp4U1VIcUZhdApS" "d1FSWEFJMDRDWjZja0RiMFJpdzdpMHl2anJGaEJvbTlsUEtxNElrWFpHZ1M1TVJsMHBSZ0FaVHFI" "RU1sdjh6Cm9YK0tjRzltZnlRSUh0cGtWdVNISHNRandWb3A3Zk1uVDdLQ1EzYlBJK2ZnTW1BZyto" "MUlSMTlEbTBKTSs5emwKdTM5ajBJYmt5dHJzeXN0R00rcFRSYmRwN3MybGd0T01DRnQwK3dJREFR" "QUJBb0lCQUROVFNPMnV2bG1sT1hnbgpES0RKWlRpdVlLYVh4RnJKVE94L1JFVXhnK3g5WFlKdExN" "ZU05alZKbnBLZ2NlRnJsRkhBSERrWTVCdU44eE5YCnVnbXNmejZXOEJaMmVRc2dNb1JOSXVZdjFZ" "SG9wVXlMVy9tU2cxRk5Iempzdy9QYjJrR3ZJcDRLcGdvcHYzb0wKbmFDa3JtQnRzSEorSGsvMmhV" "cGw5Y0U4aU13VldjVmV2THp5SGk5OGpOeTFJRGRJUGhSdGwwZGhNaXFDNU1Scgo0Z0xKNWdOa0xZ" "WDd4ZjN0dzVIbWZrL2JWTlByb3FaWERJUVZJN3JGdkl0WDU4Nm52UTNMTlFrbVcvRDJTaFpmCjNG" "RXFNdTZFZEEyWWNjNFVaZ0FsUU5HVjBWQnJXV1ZYaXpPUSs5Z2pMbkJrM2tKanFmaWdDVTZORzk0" "YlRKK0gKMFlJaHNHRUNnWUVBd2RTU3l1TVNPWGd6WlE3VnYrR3NObi83aXZpL0g4ZWIvbER6a3Nx" "Uy9Kcm9BMmNpQW1IRwoyT0YzMGVVSktSZytTVHFCVHBPZlhnUzRRVWE4UUxTd0JTbndjdzY1Nzl4" "OWJZR1VocUQyWXBhdzl1Q25PdWtBCkN3d2dnWjljRG1GMHRiNXJZanFrVzNiRlBxa0NuVEdiMHls" "TUZhWVJoUkRVMjBpRzV0OFBRY2tDZ1lFQXlRRU0KS0sxOEZMUVVLaXZHclFnUDVJYjZJQzNteXps" "SEd4RHpmb2JYR3BhUW50Rm5IWTdDeHAvNkJCdG1BU3p0OUp4dQpldG5yZXZtenJiS3FzTFRKU2cz" "aXZiaXEwWVRMQUoxRnNackNwNzFkeDQ5WVIvNW85UUZpcTBuUW9LbndVVmViCi9ockRqTUFva05r" "akZMNXZvdVhPNzExR1NTNll5TTRXekFLWkFxTUNnWUVBaHFHeGFHMDZqbUo0U0Z4NmliSWwKblNG" "ZVJoUXJKTmJQK21DZUhycklSOThOQXJnUy9sYU4rTHo3TGZhSlcxcjBnSWE3cENtVGk0bDV0aFY4" "MHZEdQpSbGZ3Sk9yNHFhdWNENER1K21nNVd4ZFNTZGlYTDZzQmxhclJ0VmRNYU15MmRUcVRlZ0pE" "Z1NoSkx4SFR0LzNxClAweXpCV0o1VHRUM0ZHMFhEcXVtL0VrQ2dZQVlOSHdXV2UzYlFHUTlQOUJJ" "L2ZPTC9ZVVpZdTJzQTFYQXVLWFoKMHJzTWhKMGR3dkc3NlhrakdoaXRiZTgyclFacXNudkxaM3Fu" "OEhIbXRPRkJMa1FmR3RUM0s4bkdPVXVJNDJlRgpIN0haS1VDbHkybENJaXpaZERWQmt6NEFXdmFK" "bFJjLzNsRTJIZDNFczZFNTJrVHZST1ZLaGR6MDZ4dVM4dDVqCjZ0d3FLUUtCZ1FDMDFBZWlXTDZS" "em8reVpOelZnYnBlZURvZ2FaejVkdG1VUkRnQ1lIOHlGWDVlb0NLTEhmbkkKMm5ESW9xcGFIWTBM" "dVgrZGludUgralA0dGx5bmRiYzJtdVhuSGQ5cjBhdHl0eEE2OWF5M3NTQTVXRnRmaTRlZgpFU0Vs" "R082cVhFQTgyMVJwUXArMit1aEw5MCtpQzI5NGNQcWxTNUxEbXZUTXlwVkRIenJ4UFE9PQotLS0t" "LUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo="; #if Z_FEATURE_MATCHING == 1 static void matching_status_handler(const z_matching_status_t *matching_status, void *arg) { (void)arg; if (matching_status->matching) { printf("Publisher has matching subscribers.\n"); } else { printf("Publisher has NO MORE matching subscribers.\n"); } } #endif int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-pub"; char *const default_value = "Pub from Pico!"; char *value = default_value; int repeat = 2147483647; bool add_matching_listener = false; bool has_listen = false; bool enable_mtls = false; bool enable_verify_name = false; const char *ca_path = NULL; const char *listen_key_path = NULL; const char *listen_cert_path = NULL; const char *client_key_path = NULL; const char *client_cert_path = NULL; z_owned_config_t config; z_config_default(&config); int opt; while ((opt = getopt(argc, argv, "hk:v:e:m:l:n:aC:P:Q:R:S:MV")) != -1) { switch (opt) { case 'h': printf("Usage: %s [options]\n", argv[0]); printf(" -k Key expression (default: demo/example/zenoh-pico-pub)\n"); printf(" -v Payload value (default: \"Pub from Pico!\")\n"); printf(" -n Number of samples to publish (default: infinite)\n"); printf(" -e Add connect locator (e.g., tls/127.0.0.1:7447)\n"); printf(" -l Set listen locator (e.g., tls/0.0.0.0:7447)\n"); printf(" -m Session mode: client or peer (default: client)\n"); printf(" -C CA bundle path (optional; inline bundle used otherwise)\n"); printf(" -P Listener private key path (peers)\n"); printf(" -Q Listener certificate path (peers)\n"); printf(" -R Client private key path (mTLS)\n"); printf(" -S Client certificate path (mTLS)\n"); printf(" -M Enable mutual TLS (client authentication)\n"); printf(" -V Enable hostname verification on connect\n"); printf(" -a Enable matching listener\n"); printf(" -h Show this help message\n"); z_drop(z_move(config)); return 0; case 'k': keyexpr = optarg; break; case 'v': value = optarg; break; case 'e': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to add connect locator\n"); z_drop(z_move(config)); return -1; } break; case 'm': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to set session mode\n"); z_drop(z_move(config)); return -1; } break; case 'l': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to set listen locator\n"); z_drop(z_move(config)); return -1; } has_listen = true; break; case 'n': repeat = atoi(optarg); break; case 'a': add_matching_listener = true; break; case 'C': ca_path = optarg; break; case 'P': listen_key_path = optarg; break; case 'Q': listen_cert_path = optarg; break; case 'R': client_key_path = optarg; break; case 'S': client_cert_path = optarg; break; case 'M': enable_mtls = true; break; case 'V': enable_verify_name = true; break; default: z_drop(z_move(config)); return -1; } } if (ca_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY, ca_path) != Z_OK) { fprintf(stderr, "Failed to set CA certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY, SERVER_CA_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline CA certificate\n"); z_drop(z_move(config)); return -1; } } if (has_listen) { if (listen_key_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_KEY, listen_key_path) != Z_OK) { fprintf(stderr, "Failed to set listener private key path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY, SERVER_KEY_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline listener private key\n"); z_drop(z_move(config)); return -1; } } if (listen_cert_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_CERTIFICATE_KEY, listen_cert_path) != Z_OK) { fprintf(stderr, "Failed to set listener certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY, SERVER_CERT_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline listener certificate\n"); z_drop(z_move(config)); return -1; } } } if (enable_mtls) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ENABLE_MTLS_KEY, "true") != Z_OK) { fprintf(stderr, "Failed to enable mTLS\n"); z_drop(z_move(config)); return -1; } if (client_key_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_KEY, client_key_path) != Z_OK) { fprintf(stderr, "Failed to set client private key path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_BASE64_KEY, SERVER_KEY_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline client private key\n"); z_drop(z_move(config)); return -1; } } if (client_cert_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_CERTIFICATE_KEY, client_cert_path) != Z_OK) { fprintf(stderr, "Failed to set client certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY, SERVER_CERT_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline client certificate\n"); z_drop(z_move(config)); return -1; } } } if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY, enable_verify_name ? "true" : "false") != Z_OK) { fprintf(stderr, "Failed to configure hostname verification\n"); z_drop(z_move(config)); return -1; } printf("Opening session...\n"); z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); z_drop(z_move(session)); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(session), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); z_drop(z_move(session)); return -1; } if (add_matching_listener) { #if Z_FEATURE_MATCHING == 1 z_owned_closure_matching_status_t callback; z_closure(&callback, matching_status_handler, NULL, NULL); z_publisher_declare_background_matching_listener(z_loan(pub), z_move(callback)); #else printf("ERROR: Zenoh pico was compiled without Z_FEATURE_MATCHING but this example requires it.\n"); z_drop(z_move(pub)); z_drop(z_move(session)); return -2; #endif } printf("Press CTRL-C to quit...\n"); for (int idx = 0; idx < repeat; ++idx) { z_sleep_s(1); char buf[256]; snprintf(buf, sizeof(buf), "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); } z_drop(z_move(pub)); z_drop(z_move(session)); return 0; } #else int main(void) { #if Z_FEATURE_PUBLICATION != 1 printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); #elif Z_FEATURE_LINK_TLS != 1 printf("ERROR: Zenoh pico was compiled without Z_FEATURE_LINK_TLS but this example requires it.\n"); #endif return -2; } #endif ================================================ FILE: examples/unix/c11/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, size_t *interval, size_t *ring_size); int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; size_t interval = 5000; size_t ring_size = 3; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &interval, &ring_size); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, ring_size); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Pulling data every %zu ms... Ring size: %zd\n", interval, ring_size); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", interval); z_sleep_ms(interval); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, size_t *interval, size_t *ring_size) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:i:s:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'i': *interval = (size_t)atoi(optarg); break; case 's': *ring_size = (size_t)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'i' || optopt == 's' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_put.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include "zenoh-pico/api/macros.h" #include "zenoh-pico/api/types.h" #if Z_FEATURE_PUBLICATION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value); int main(int argc, char **argv) { char *keyexpr = "demo/example/zenoh-pico-put"; char *const default_value = "Pub from Pico!"; char *value = default_value; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring key expression '%s'...\n", keyexpr); z_view_keyexpr_t vke; if (z_view_keyexpr_from_str(&vke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_owned_keyexpr_t ke; if (z_declare_keyexpr(z_loan(s), &ke, z_loan(vke)) < 0) { z_drop(z_move(s)); return -1; } // Create payload z_owned_bytes_t payload; z_bytes_from_static_str(&payload, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, value); if (z_put(z_loan(s), z_loan(ke), z_move(payload), NULL) < 0) { printf("Oh no! Put has failed...\n"); } // Clean up z_undeclare_keyexpr(z_loan(s), z_move(ke)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, char **value) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_querier.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **selector, char **value, int *n, int *timeout_ms, bool *add_matching_listener); #if Z_FEATURE_MATCHING == 1 void matching_status_handler(const z_matching_status_t *matching_status, void *arg) { (void)arg; if (matching_status->matching) { printf("Querier has matching queryable.\n"); } else { printf("Querier has NO MORE matching queryables.\n"); } } #endif int main(int argc, char **argv) { char *selector = "demo/example/**"; char *value = NULL; int n = INT_MAX; int timeout_ms = 0; bool add_matching_listener = false; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &selector, &value, &n, &timeout_ms, &add_matching_listener); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } const char *ke = selector; size_t ke_len = strlen(ke); const char *params = strchr(selector, '?'); if (params != NULL) { ke_len = (size_t)(params - ke); params += 1; } z_view_keyexpr_t keyexpr; if (z_view_keyexpr_from_substr(&keyexpr, ke, ke_len) < 0) { printf("%.*s is not a valid key expression\n", (int)ke_len, ke); exit(-1); } printf("Declaring Querier on '%s'...\n", ke); z_owned_querier_t querier; z_querier_options_t opts; z_querier_options_default(&opts); opts.timeout_ms = (uint64_t)timeout_ms; if (z_declare_querier(z_loan(s), &querier, z_loan(keyexpr), &opts) < 0) { printf("Unable to declare Querier for key expression!\n"); exit(-1); } if (add_matching_listener) { #if Z_FEATURE_MATCHING == 1 z_owned_closure_matching_status_t callback; z_closure(&callback, matching_status_handler, NULL, NULL); z_querier_declare_background_matching_listener(z_loan(querier), z_move(callback)); #else printf("ERROR: Zenoh pico was compiled without Z_FEATURE_MATCHING but this example requires it.\n"); return -2; #endif } printf("Press CTRL-C to quit...\n"); char buf[256]; for (int idx = 0; idx != n; ++idx) { z_sleep_s(1); sprintf(buf, "[%4d] %s", idx, value ? value : ""); printf("Querying '%s' with payload '%s'...\n", selector, buf); z_querier_get_options_t get_options; z_querier_get_options_default(&get_options); if (value != NULL) { z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); get_options.payload = z_move(payload); } z_owned_fifo_handler_reply_t handler; z_owned_closure_reply_t closure; z_fifo_channel_reply_new(&closure, &handler, 16); z_querier_get(z_loan(querier), params, z_move(closure), &get_options); z_owned_reply_t reply; for (z_result_t res = z_recv(z_loan(handler), &reply); res == Z_OK; res = z_recv(z_loan(handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(">> Received an error\n"); } z_drop(z_move(reply)); } z_drop(z_move(handler)); } z_drop(z_move(querier)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **selector, char **value, int *n, int *timeout_ms, bool *add_matching_listener) { int opt; while ((opt = getopt(argc, argv, "s:v:e:m:l:n:t:a")) != -1) { switch (opt) { case 's': *selector = optarg; break; case 'v': *value = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case 't': *timeout_ms = atoi(optarg); break; case 'a': *add_matching_listener = true; break; case '?': if (optopt == 's' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 't' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include typedef enum _reply_kind_e { REPLY_DATA, REPLY_DELETE, REPLY_ERR } _reply_kind_e; #if Z_FEATURE_QUERYABLE == 1 char *keyexpr = "demo/example/zenoh-pico-queryable"; char *value = "Queryable from Pico!"; char *error = "Demo error"; static int msg_nb = 0; static _reply_kind_e reply_kind = REPLY_DATA; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val, int *n, _reply_kind_e *reply_kind); void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); switch (reply_kind) { case REPLY_DATA: { // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); break; } case REPLY_DELETE: { z_query_reply_del(query, z_query_keyexpr(query), NULL); break; } case REPLY_ERR: { // Reply error encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, error); z_query_reply_err(query, z_move(reply_payload), NULL); break; } } msg_nb++; } int main(int argc, char **argv) { int n = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &n, &reply_kind); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Creating Queryable on '%s'...\n", keyexpr); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { if ((n != 0) && (msg_nb >= n)) { break; } sleep(1); } z_drop(z_move(qable)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val, int *n, _reply_kind_e *repknd) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'v': *val = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case 'd': *repknd = REPLY_DELETE; break; case 'f': *repknd = REPLY_ERR; break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_queryable_attachment.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include typedef struct kv_pair_t { z_owned_string_t key; z_owned_string_t value; } kv_pair_t; #if Z_FEATURE_QUERYABLE == 1 char *keyexpr = "demo/example/zenoh-pico-queryable"; char *const default_value = "Queryable from Pico!"; char *value = default_value; int msg_nb = 0; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val, int *n); void print_attachment(const kv_pair_t *kvp, size_t len) { printf(" with attachment:\n"); for (size_t i = 0; i < len; i++) { printf(" %zu: %.*s, %.*s\n", i, (int)z_string_len(z_loan(kvp[i].key)), z_string_data(z_loan(kvp[i].key)), (int)z_string_len(z_loan(kvp[i].value)), z_string_data(z_loan(kvp[i].value))); } } void drop_attachment(kv_pair_t *kvp, size_t len) { for (size_t i = 0; i < len; i++) { z_drop(z_move(kvp[i].key)); z_drop(z_move(kvp[i].value)); } } void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process encoding z_owned_string_t encoding; z_encoding_to_string(z_query_encoding(query), &encoding); printf(" with encoding: %.*s\n", (int)z_string_len(z_loan(encoding)), z_string_data(z_loan(encoding))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); z_drop(z_move(encoding)); // Check attachment const z_loaned_bytes_t *attachment = z_query_attachment(query); ze_deserializer_t deserializer = ze_deserializer_from_bytes(attachment); size_t attachment_len; if (ze_deserializer_deserialize_sequence_length(&deserializer, &attachment_len) == Z_OK) { kv_pair_t *kvp = (kv_pair_t *)malloc(sizeof(kv_pair_t) * attachment_len); for (size_t i = 0; i < attachment_len; ++i) { ze_deserializer_deserialize_string(&deserializer, &kvp[i].key); ze_deserializer_deserialize_string(&deserializer, &kvp[i].value); } if (attachment_len > 0) { print_attachment(kvp, attachment_len); } drop_attachment(kvp, attachment_len); free(kvp); } // Reply payload z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply_options_t options; z_query_reply_options_default(&options); // Reply attachment z_owned_bytes_t reply_attachment; kv_pair_t kvs[1]; z_string_from_str(&kvs[0].key, "reply_key", NULL, NULL); z_string_from_str(&kvs[0].value, "reply_value", NULL, NULL); ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(z_loan_mut(serializer), 1); ze_serializer_serialize_string(z_loan_mut(serializer), z_loan(kvs[0].key)); ze_serializer_serialize_string(z_loan_mut(serializer), z_loan(kvs[0].value)); ze_serializer_finish(z_move(serializer), &reply_attachment); options.attachment = z_move(reply_attachment); drop_attachment(kvs, 1); // Reply encoding z_owned_encoding_t reply_encoding; z_encoding_from_str(&reply_encoding, "zenoh/string;utf8"); options.encoding = z_move(reply_encoding); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), &options); msg_nb++; } int main(int argc, char **argv) { int n = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Creating Queryable on '%s'...\n", keyexpr); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { if ((n != 0) && (msg_nb >= n)) { break; } sleep(1); } z_drop(z_move(qable)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val, int *n) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'v': *val = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_queryable_channel.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_QUERYABLE == 1 char *keyexpr = "demo/example/zenoh-pico-queryable"; char *const default_value = "Queryable from Pico!"; char *value = default_value; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val); int main(int argc, char **argv) { z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &value); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Creating Queryable on '%s'...\n", keyexpr); z_owned_closure_query_t closure; z_owned_ring_handler_query_t handler; z_ring_channel_query_new(&closure, &handler, 10); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } z_owned_query_t query; for (z_result_t res = z_recv(z_loan(handler), &query); res == Z_OK; res = z_recv(z_loan(handler), &query)) { const z_loaned_query_t *q = z_loan(query); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(q), &keystr); z_view_string_t params; z_query_parameters(q, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(z_loan(query)), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); z_query_reply_options_t options; z_query_reply_options_default(&options); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply(q, z_query_keyexpr(q), z_move(reply_payload), &options); z_drop(z_move(query)); } z_drop(z_move(handler)); z_drop(z_move(qable)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, char **val) { int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'v': *val = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_queryable_lat.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include "zenoh-pico.h" typedef struct { uint8_t *payload; size_t payload_len; } z_stats_t; #define TEST_RUN_TIME_S 20 #if Z_FEATURE_QUERYABLE == 1 && Z_FEATURE_MULTI_THREAD == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, size_t *pkt_size); void query_handler(z_loaned_query_t *query, void *ctx) { z_stats_t *stats = (z_stats_t *)ctx; // Reply z_owned_bytes_t reply_payload; z_bytes_from_static_buf(&reply_payload, stats->payload, stats->payload_len); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); z_drop(z_move(reply_payload)); } void drop_stats(void *context) { z_stats_t *stats = (z_stats_t *)context; free(stats->payload); free(context); } void *stop_task(void *ctx) { z_sleep_s(TEST_RUN_TIME_S); _Atomic int *stop_flag = (_Atomic int *)ctx; atomic_store_explicit(stop_flag, 1, memory_order_relaxed); return NULL; } int main(int argc, char **argv) { char *keyexpr = "lat"; size_t payload_len = 8; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &payload_len); if (ret != 0) { return ret; } z_stats_t *context = malloc(sizeof(z_stats_t)); context->payload_len = payload_len; context->payload = (uint8_t *)malloc(context->payload_len); memset(context->payload, 1, context->payload_len); // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Declare Queryable z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, keyexpr); z_owned_closure_query_t callback; z_closure(&callback, query_handler, drop_stats, (void *)context); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } _Atomic int stop_flag; atomic_store_explicit(&stop_flag, 0, memory_order_relaxed); pthread_t task; pthread_create(&task, NULL, stop_task, &stop_flag); // Listen until stopped while (atomic_load_explicit(&stop_flag, memory_order_relaxed) == 0) { z_sleep_s(1); } pthread_join(task, NULL); // Clean up z_drop(z_move(qable)); z_drop(z_move(s)); exit(0); } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, size_t *pkt_size) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:s:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 's': *pkt_size = (size_t)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 's') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } int main(int argc, char **argv) { (void)(argc); (void)(argv); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_move(config), z_move(closure), NULL); sleep(1); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 static int msg_nb = 0; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n); void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; int n = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { if ((n != 0) && (msg_nb >= n)) { break; } sleep(1); } // Clean up z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, int *n) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:n:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_attachment.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include typedef struct kv_pair_t { z_owned_string_t key; z_owned_string_t value; } kv_pair_t; #if Z_FEATURE_SUBSCRIPTION == 1 static int msg_nb = 0; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n); void print_attachment(const kv_pair_t *kvp, size_t len) { printf(" with attachment:\n"); for (size_t i = 0; i < len; i++) { printf(" %zu: %.*s, %.*s\n", i, (int)z_string_len(z_loan(kvp[i].key)), z_string_data(z_loan(kvp[i].key)), (int)z_string_len(z_loan(kvp[i].value)), z_string_data(z_loan(kvp[i].value))); } } void drop_attachment(kv_pair_t *kvp, size_t len) { for (size_t i = 0; i < len; i++) { z_drop(z_move(kvp[i].key)); z_drop(z_move(kvp[i].value)); } } void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); z_owned_string_t encoding; z_encoding_to_string(z_sample_encoding(sample), &encoding); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); printf(" with encoding: %.*s\n", (int)z_string_len(z_loan(encoding)), z_string_data(z_loan(encoding))); // Check timestamp const z_timestamp_t *ts = z_sample_timestamp(sample); if (ts != NULL) { printf(" with timestamp: %" PRIu64 "\n", z_timestamp_ntp64_time(ts)); } // Check attachment const z_loaned_bytes_t *attachment = z_sample_attachment(sample); ze_deserializer_t deserializer = ze_deserializer_from_bytes(attachment); size_t attachment_len; if (ze_deserializer_deserialize_sequence_length(&deserializer, &attachment_len) == Z_OK) { kv_pair_t *kvp = (kv_pair_t *)malloc(sizeof(kv_pair_t) * attachment_len); for (size_t i = 0; i < attachment_len; ++i) { ze_deserializer_deserialize_string(&deserializer, &kvp[i].key); ze_deserializer_deserialize_string(&deserializer, &kvp[i].value); } if (attachment_len > 0) { print_attachment(kvp, attachment_len); } drop_attachment(kvp, attachment_len); free(kvp); } z_drop(z_move(value)); z_drop(z_move(encoding)); msg_nb++; } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; int n = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { if ((n != 0) && (msg_nb >= n)) { break; } sleep(1); } // Clean up z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:n:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_channel.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr); int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; z_fifo_channel_sample_new(&closure, &handler, 3); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } z_owned_sample_t sample; for (z_result_t res = z_recv(z_loan(handler), &sample); res == Z_OK; res = z_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_LIVELINESS == 1 static volatile int msg_nb = 0; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n, bool *history); void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t key_string; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &key_string); switch (z_sample_kind(sample)) { case Z_SAMPLE_KIND_PUT: printf(">> [LivelinessSubscriber] New alive token ('%.*s')\n", (int)z_string_len(z_loan(key_string)), z_string_data(z_loan(key_string))); break; case Z_SAMPLE_KIND_DELETE: printf(">> [LivelinessSubscriber] Dropped token ('%.*s')\n", (int)z_string_len(z_loan(key_string)), z_string_data(z_loan(key_string))); break; } msg_nb++; } int main(int argc, char **argv) { char *keyexpr = "group1/**"; bool history = false; int n = 0; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &n, &history); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } #if Z_FEATURE_ADMIN_SPACE == 1 // Start admin space if (zp_start_admin_space(z_loan_mut(s)) < 0) { printf("Unable to start admin space\n"); z_drop(z_move(s)); return -1; } #endif printf("Declaring liveliness subscriber on '%s'...\n", keyexpr); z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); z_owned_subscriber_t sub; z_liveliness_subscriber_options_t sub_opt; z_liveliness_subscriber_options_default(&sub_opt); sub_opt.history = history; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_liveliness_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), &sub_opt) < 0) { printf("Unable to declare liveliness subscriber.\n"); z_drop(z_move(s)); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { if (n != 0 && msg_nb >= n) { break; } z_sleep_s(1); } // Clean up z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n, bool *history) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:n:h")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case 'h': *history = true; break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION and Z_FEATURE_LIVELINESS but this example " "requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 0 static int msg_nb = 0; static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n); void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; int n = 2147483647; // max int value by default z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &n); if (ret != 0) { return ret; } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (msg_nb < n) { z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **keyexpr, int *n) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:n:")) != -1) { switch (opt) { case 'k': *keyexpr = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'n': *n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_SUBSCRIPTION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_thr.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include "zenoh-pico.h" #define TEST_RUN_TIME_S 23 typedef struct { _Atomic unsigned long count; } z_stats_t; #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 1 static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, unsigned long *freq, bool *is_peer); void on_sample(z_loaned_sample_t *sample, void *context) { (void)sample; z_stats_t *stats = (z_stats_t *)context; atomic_fetch_add_explicit(&stats->count, 1, memory_order_relaxed); } void drop_stats(void *context) { free(context); } unsigned long cas_loop(z_stats_t *ctx, unsigned long value) { unsigned long prev_val = atomic_load_explicit(&ctx->count, memory_order_relaxed); unsigned long curr_val = prev_val; while (curr_val != value) { prev_val = curr_val; if (atomic_compare_exchange_weak_explicit(&ctx->count, &curr_val, value, memory_order_acquire, memory_order_relaxed)) { return prev_val; } } return prev_val; } void *stop_task(void *ctx) { z_sleep_s(TEST_RUN_TIME_S); bool *stop_flag = (bool *)ctx; *stop_flag = true; return NULL; } int main(int argc, char **argv) { char *keyexpr = "thr"; unsigned long frequency = 10; bool is_peer = false; z_owned_config_t config; z_config_default(&config); int ret = parse_args(argc, argv, &config, &keyexpr, &frequency, &is_peer); if (ret != 0) { return ret; } z_stats_t *context = malloc(sizeof(z_stats_t)); atomic_store_explicit(&context->count, 0, memory_order_relaxed); // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Declare Subscriber/resource z_owned_closure_sample_t callback; z_closure(&callback, on_sample, drop_stats, (void *)context); z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_background_subscriber(z_loan(s), z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create subscriber.\n"); exit(-1); } bool stop_flag = false; pthread_t task; pthread_create(&task, NULL, stop_task, &stop_flag); unsigned long sleep_dur_us = 1000000 / frequency; // Listen until stopped while (!stop_flag) { z_clock_t start = z_clock_now(); z_sleep_us(sleep_dur_us); unsigned long elapsed = z_clock_elapsed_us(&start); // Clear count unsigned long count = cas_loop(context, 0); if (count > 0) { printf("%.3lf\n", (double)count * 1000000.0 / (double)elapsed); } } // Clean up z_drop(z_move(s)); exit(0); } // Note: All args can be specified multiple times. For "-e" it will append the list of endpoints, for the other it will // simply replace the previous value. static int parse_args(int argc, char **argv, z_owned_config_t *config, char **ke, unsigned long *freq, bool *is_peer) { int opt; while ((opt = getopt(argc, argv, "k:e:m:l:f:")) != -1) { switch (opt) { case 'k': *ke = optarg; break; case 'e': zp_config_insert(z_loan_mut(*config), Z_CONFIG_CONNECT_KEY, optarg); break; case 'm': zp_config_insert(z_loan_mut(*config), Z_CONFIG_MODE_KEY, optarg); if (strcmp(optarg, "peer") == 0) { *is_peer = true; } break; case 'l': zp_config_insert(z_loan_mut(*config), Z_CONFIG_LISTEN_KEY, optarg); break; case 'f': *freq = (unsigned long)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'f') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c11/z_sub_tls.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_LINK_TLS == 1 static const char SERVER_CA_PEM[] = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURTekNDQWpPZ0F3SUJBZ0lJVGN3djFOMTBu" "cUV3RFFZSktvWklodmNOQVFFTEJRQXdJREVlTUJ3R0ExVUUKQXhNVmJXbHVhV05oSUhKdmIzUWdZ" "MkVnTkdSall6Sm1NQ0FYRFRJek1ETXdOakUyTkRFd05sb1lEekl4TWpNdwpNekEyTVRZME1UQTJX" "akFnTVI0d0hBWURWUVFERXhWdGFXNXBZMkVnY205dmRDQmpZU0EwWkdOak1tWXdnZ0VpCk1BMEdD" "U3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzJXVWdON05NbFhJa25ldzFjWGlUV0dt" "UzAKMVQxRWpjTk5EQXE3RHFaNy9aVlhyakQ0N3l4VHQ1RU9pT1hLL2NJTktOdzRacS9NS1F2cTlx" "dStPYXg0bHdpVgpIYTBpOFNoR0xTdVlJMUhCbFh1NE1tdmRHKzMvU2p3WW9Hc0dhU2hyMHkvUUd6" "RDNjRCtEUVpnL1JhYUlQSGxPCk1kbWlVWHhrTWN5NHFhMGhGSjFpbWxKZHEvNlRseDQ2WCswdlJD" "aDhua2Vrdk9aUit0N1o1VTRqbjRYRTU0S2wKMFBpd2N5WDh2ZkRaM2VwYS9GU0hadlZRaWVNL2c1" "WWg5T2pJS0NrZFdSZzd0RDBJRUdzYVcxMXRFUEo1U2lRcgptRHFkUm5lTXpaS3FZMHhDK1FxWFN2" "SWx6cE9qaXU4UFlReDd4dWdhVUZFL25wS1JRZHZoOG9qSEpNZE5BZ01CCkFBR2pnWVl3Z1lNd0Rn" "WURWUjBQQVFIL0JBUURBZ0tFTUIwR0ExVWRKUVFXTUJRR0NDc0dBUVVGQndNQkJnZ3IKQmdFRkJR" "Y0RBakFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9BZ0VBTUIwR0ExVWREZ1FXQkJUWDQ2K3ArUG8xbnBF" "NgpRTFE3bU1JKzgzczZxREFmQmdOVkhTTUVHREFXZ0JUWDQ2K3ArUG8xbnBFNlFMUTdtTUkrODNz" "NnFEQU5CZ2txCmhraUc5dzBCQVFzRkFBT0NBUUVBYU4wSXZFQzY3N1BML0pYek1yWGN5QlY4OEl2" "aW1sWU4wekN0NDhHWWxobXgKdkwxWVVERkxKRUI3SitkeUVSR0U1TjZCS0tER2JsQzRXaVRGZ0RN" "TGNIRnNNR1JjMHY3ektQRjFQU0J3UllKaQp1YkFta3dkdW5HRzVwRFBVWXRURURQWE1sZ0NsWjBZ" "eXFTRkpNT3FBNEl6UWc2ZXhWalh0VXhQcXp4Tmh5QzdTCnZsZ1V3UGJYNDZ1Tmk1ODFhOStMczJW" "M2ZnMFpuaGtUU2N0WVpIR1pOZWgwTnNmN0FtOHhkVURZRy9iWmNWZWYKamJROWdwQ2hvc2RqRjBC" "Z2JsbzdIU1VjdC8yVmErWWxZd1crV0ZqSlg4azRvTjZaVTVXNXhoZGZPOEN6bWd3awpVUzVrSi8r" "MU0wdVI4elVoWkhMNjFGYnNkUHhFaitmWUtySHY0d29vK0E9PQotLS0tLUVORCBDRVJUSUZJQ0FU" "RS0tLS0tCg=="; static const char SERVER_CERT_PEM[] = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhhZ0F3SUJBZ0lJVzFtQXRKV0pB" "Sll3RFFZSktvWklodmNOQVFFTEJRQXdJREVlTUJ3R0ExVUUKQXhNVmJXbHVhV05oSUhKdmIzUWdZ" "MkVnTkdSall6Sm1NQ0FYRFRJek1ETXdOakUyTkRFd05sb1lEekl4TWpNdwpNekEyTVRZME1UQTJX" "akFVTVJJd0VBWURWUVFERXdsc2IyTmhiR2h2YzNRd2dnRWlNQTBHQ1NxR1NJYjNEUUVCCkFRVUFB" "NElCRHdBd2dnRUtBb0lCQVFDWU1MSktvb2MrWVJsS0VNZmVWMDlwWDlteUgzNGVVY1V1VDBmWFM4" "bG0KUGxaL05XN21tNWxEd2E4RVVnNjFXdVhRdjJvdVFEcHRtSWNkZWIvdzRSVzkzWGZsa3luZzFY" "YmQ5MU93UUJKZAorOFpWQmp6TDdoU1JrM1FQRHF4L0NWQlUvSTFHbVhLemI2Y1d6cTFmVGtPbjFX" "TE5YZjIxSTZwNytOM3FITFBGCkpRZW9WcTFIQkJGY0FqVGdKbnB5UU52UkdMRHVMVEsrT3NXRUdp" "YjJVOHFyZ2lSZGthQkxreEdYU2xHQUJsT28KY1F5Vy96T2hmNHB3YjJaL0pBZ2UybVJXNUljZXhD" "UEJXaW50OHlkUHNvSkRkczhqNStBeVlDRDZIVWhIWDBPYgpRa3o3M09XN2YyUFFodVRLMnV6S3kw" "WXo2bE5GdDJudXphV0MwNHdJVzNUN0FnTUJBQUdqZGpCME1BNEdBMVVkCkR3RUIvd1FFQXdJRm9E" "QWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0RBUVlJS3dZQkJRVUhBd0l3REFZRFZSMFQKQVFIL0JB" "SXdBREFmQmdOVkhTTUVHREFXZ0JUWDQ2K3ArUG8xbnBFNlFMUTdtTUkrODNzNnFEQVVCZ05WSFJF" "RQpEVEFMZ2dsc2IyTmhiR2h2YzNRd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBeHJtUVBHNTR5" "YktnTVZsaU44Ck1nNXBvdlNkUElWVm5sVS9IT1ZHOXl4ekFPYXYveFFQMDAzTTR3cXBhdFd4STh0" "UjFQY0x1WmYwRVBtY2RKZ2IKdFZsOW5aTVZadHZlUW5ZTWxVOFBwa0VWdTU2Vk00WnIzckg5bGlQ" "UmxyMEpFQVhPRGRLdzc2a1dLem1kcVdaLwpyemh1cDNFazdpRVg2VDVqL2NQVXZUV3RNRDRWRUsy" "STdmZ29LU0hJWDhNSVZ6cU03Y3Vib0dXUHRTM2VSTlhsCk1ndmFoQTRUd0xFWFBFZStWMVdBcTZu" "U2I0ZzJxU1hXSURwSXN5L08xV0dTL3p6Um5Ldlh1OS85TmtYV3FaTWwKQzFMU3BpaVFVYVJTZ2xP" "dllmL1p4NnIrNEJPUzRPYWFBcndIa2VjWlFxQlNDY0JMRUF5Yi9GYWFYZEJvd0kwVQpQUTQ9Ci0t" "LS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"; static const char SERVER_KEY_PEM[] = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBbURDeVNxS0hQ" "bUVaU2hESDNsZFBhVi9ac2g5K0hsSEZMazlIMTB2SlpqNVdmelZ1CjVwdVpROEd2QkZJT3RWcmww" "TDlxTGtBNmJaaUhIWG0vOE9FVnZkMTM1Wk1wNE5WMjNmZFRzRUFTWGZ2R1ZRWTgKeSs0VWtaTjBE" "dzZzZndsUVZQeU5ScGx5czIrbkZzNnRYMDVEcDlWaXpWMzl0U09xZS9qZDZoeXp4U1VIcUZhdApS" "d1FSWEFJMDRDWjZja0RiMFJpdzdpMHl2anJGaEJvbTlsUEtxNElrWFpHZ1M1TVJsMHBSZ0FaVHFI" "RU1sdjh6Cm9YK0tjRzltZnlRSUh0cGtWdVNISHNRandWb3A3Zk1uVDdLQ1EzYlBJK2ZnTW1BZyto" "MUlSMTlEbTBKTSs5emwKdTM5ajBJYmt5dHJzeXN0R00rcFRSYmRwN3MybGd0T01DRnQwK3dJREFR" "QUJBb0lCQUROVFNPMnV2bG1sT1hnbgpES0RKWlRpdVlLYVh4RnJKVE94L1JFVXhnK3g5WFlKdExN" "ZU05alZKbnBLZ2NlRnJsRkhBSERrWTVCdU44eE5YCnVnbXNmejZXOEJaMmVRc2dNb1JOSXVZdjFZ" "SG9wVXlMVy9tU2cxRk5Iempzdy9QYjJrR3ZJcDRLcGdvcHYzb0wKbmFDa3JtQnRzSEorSGsvMmhV" "cGw5Y0U4aU13VldjVmV2THp5SGk5OGpOeTFJRGRJUGhSdGwwZGhNaXFDNU1Scgo0Z0xKNWdOa0xZ" "WDd4ZjN0dzVIbWZrL2JWTlByb3FaWERJUVZJN3JGdkl0WDU4Nm52UTNMTlFrbVcvRDJTaFpmCjNG" "RXFNdTZFZEEyWWNjNFVaZ0FsUU5HVjBWQnJXV1ZYaXpPUSs5Z2pMbkJrM2tKanFmaWdDVTZORzk0" "YlRKK0gKMFlJaHNHRUNnWUVBd2RTU3l1TVNPWGd6WlE3VnYrR3NObi83aXZpL0g4ZWIvbER6a3Nx" "Uy9Kcm9BMmNpQW1IRwoyT0YzMGVVSktSZytTVHFCVHBPZlhnUzRRVWE4UUxTd0JTbndjdzY1Nzl4" "OWJZR1VocUQyWXBhdzl1Q25PdWtBCkN3d2dnWjljRG1GMHRiNXJZanFrVzNiRlBxa0NuVEdiMHls" "TUZhWVJoUkRVMjBpRzV0OFBRY2tDZ1lFQXlRRU0KS0sxOEZMUVVLaXZHclFnUDVJYjZJQzNteXps" "SEd4RHpmb2JYR3BhUW50Rm5IWTdDeHAvNkJCdG1BU3p0OUp4dQpldG5yZXZtenJiS3FzTFRKU2cz" "aXZiaXEwWVRMQUoxRnNackNwNzFkeDQ5WVIvNW85UUZpcTBuUW9LbndVVmViCi9ockRqTUFva05r" "akZMNXZvdVhPNzExR1NTNll5TTRXekFLWkFxTUNnWUVBaHFHeGFHMDZqbUo0U0Z4NmliSWwKblNG" "ZVJoUXJKTmJQK21DZUhycklSOThOQXJnUy9sYU4rTHo3TGZhSlcxcjBnSWE3cENtVGk0bDV0aFY4" "MHZEdQpSbGZ3Sk9yNHFhdWNENER1K21nNVd4ZFNTZGlYTDZzQmxhclJ0VmRNYU15MmRUcVRlZ0pE" "Z1NoSkx4SFR0LzNxClAweXpCV0o1VHRUM0ZHMFhEcXVtL0VrQ2dZQVlOSHdXV2UzYlFHUTlQOUJJ" "L2ZPTC9ZVVpZdTJzQTFYQXVLWFoKMHJzTWhKMGR3dkc3NlhrakdoaXRiZTgyclFacXNudkxaM3Fu" "OEhIbXRPRkJMa1FmR3RUM0s4bkdPVXVJNDJlRgpIN0haS1VDbHkybENJaXpaZERWQmt6NEFXdmFK" "bFJjLzNsRTJIZDNFczZFNTJrVHZST1ZLaGR6MDZ4dVM4dDVqCjZ0d3FLUUtCZ1FDMDFBZWlXTDZS" "em8reVpOelZnYnBlZURvZ2FaejVkdG1VUkRnQ1lIOHlGWDVlb0NLTEhmbkkKMm5ESW9xcGFIWTBM" "dVgrZGludUgralA0dGx5bmRiYzJtdVhuSGQ5cjBhdHl0eEE2OWF5M3NTQTVXRnRmaTRlZgpFU0Vs" "R082cVhFQTgyMVJwUXArMit1aEw5MCtpQzI5NGNQcWxTNUxEbXZUTXlwVkRIenJ4UFE9PQotLS0t" "LUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo="; static int received_count = 0; static void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)ctx; z_view_string_t key; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &key); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(key)), z_string_data(z_loan(key)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); received_count++; } int main(int argc, char **argv) { char *keyexpr = "demo/example/**"; int max_msgs = 0; bool has_listen = false; bool enable_mtls = false; bool enable_verify_name = false; const char *ca_path = NULL; const char *listen_key_path = NULL; const char *listen_cert_path = NULL; const char *client_key_path = NULL; const char *client_cert_path = NULL; z_owned_config_t config; z_config_default(&config); int opt; while ((opt = getopt(argc, argv, "hk:e:m:l:n:C:P:Q:R:S:MV")) != -1) { switch (opt) { case 'h': printf("Usage: %s [options]\n", argv[0]); printf(" -k Key expression filter (default: demo/example/**)\n"); printf(" -n Stop after receiving samples (default: infinite)\n"); printf(" -e Add connect locator (e.g., tls/127.0.0.1:7447)\n"); printf(" -l Set listen locator (e.g., tls/0.0.0.0:7447)\n"); printf(" -m Session mode: client or peer (default: client)\n"); printf(" -C CA bundle path (optional; inline bundle used otherwise)\n"); printf(" -P Listener private key path (peers)\n"); printf(" -Q Listener certificate path (peers)\n"); printf(" -R Client private key path (mTLS)\n"); printf(" -S Client certificate path (mTLS)\n"); printf(" -M Enable mutual TLS\n"); printf(" -V Enable hostname verification\n"); z_drop(z_move(config)); return 0; case 'k': keyexpr = optarg; break; case 'e': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to add connect locator\n"); z_drop(z_move(config)); return -1; } break; case 'm': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to set session mode\n"); z_drop(z_move(config)); return -1; } break; case 'l': if (zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, optarg) != Z_OK) { fprintf(stderr, "Failed to set listen locator\n"); z_drop(z_move(config)); return -1; } has_listen = true; break; case 'n': max_msgs = atoi(optarg); break; case 'C': ca_path = optarg; break; case 'P': listen_key_path = optarg; break; case 'Q': listen_cert_path = optarg; break; case 'R': client_key_path = optarg; break; case 'S': client_cert_path = optarg; break; case 'M': enable_mtls = true; break; case 'V': enable_verify_name = true; break; default: z_drop(z_move(config)); return -1; } } if (ca_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY, ca_path) != Z_OK) { fprintf(stderr, "Failed to set CA certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY, SERVER_CA_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline CA certificate\n"); z_drop(z_move(config)); return -1; } } if (has_listen) { if (listen_key_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_KEY, listen_key_path) != Z_OK) { fprintf(stderr, "Failed to set listener private key path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY, SERVER_KEY_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline listener private key\n"); z_drop(z_move(config)); return -1; } } if (listen_cert_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_CERTIFICATE_KEY, listen_cert_path) != Z_OK) { fprintf(stderr, "Failed to set listener certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY, SERVER_CERT_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline listener certificate\n"); z_drop(z_move(config)); return -1; } } } if (enable_mtls) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_ENABLE_MTLS_KEY, "true") != Z_OK) { fprintf(stderr, "Failed to enable mTLS\n"); z_drop(z_move(config)); return -1; } if (client_key_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_KEY, client_key_path) != Z_OK) { fprintf(stderr, "Failed to set client private key path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_BASE64_KEY, SERVER_KEY_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline client private key\n"); z_drop(z_move(config)); return -1; } } if (client_cert_path) { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_CERTIFICATE_KEY, client_cert_path) != Z_OK) { fprintf(stderr, "Failed to set client certificate path\n"); z_drop(z_move(config)); return -1; } } else { if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY, SERVER_CERT_PEM) != Z_OK) { fprintf(stderr, "Failed to set inline client certificate\n"); z_drop(z_move(config)); return -1; } } } if (zp_config_insert(z_loan_mut(config), Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY, enable_verify_name ? "true" : "false") != Z_OK) { fprintf(stderr, "Failed to configure hostname verification\n"); z_drop(z_move(config)); return -1; } printf("Opening session...\n"); z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); z_session_drop(z_session_move(&session)); return -1; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(session), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); z_session_drop(z_session_move(&session)); return -1; } printf("Press CTRL-C to quit...\n"); while (max_msgs == 0 || received_count < max_msgs) { sleep(1); } z_drop(z_move(sub)); z_session_drop(z_session_move(&session)); return 0; } #else int main(void) { #if Z_FEATURE_SUBSCRIPTION != 1 printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); #elif Z_FEATURE_LINK_TLS != 1 printf("ERROR: Zenoh pico was compiled without Z_FEATURE_LINK_TLS but this example requires it.\n"); #endif return -2; } #endif ================================================ FILE: examples/unix/c99/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 z_owned_condvar_t cond; z_owned_mutex_t mutex; void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); z_condvar_signal(z_condvar_loan_mut(&cond)); z_condvar_drop(z_condvar_move(&cond)); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&replystr)), z_string_data(z_string_loan(&replystr))); z_string_drop(z_string_move(&replystr)); } else { printf(">> Received an error\n"); } } int main(int argc, char **argv) { const char *keyexpr = "demo/example/**"; const char *mode = "client"; const char *clocator = NULL; const char *llocator = NULL; const char *value = NULL; int opt; while ((opt = getopt(argc, argv, "k:e:m:v:l:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case 'v': value = optarg; break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'v' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_mutex_lock(z_mutex_loan_mut(&mutex)); printf("Sending Query '%s'...\n", keyexpr); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (value != NULL) { z_bytes_from_static_str(&payload, value); opts.payload = z_bytes_move(&payload); } z_owned_closure_reply_t callback; z_closure_reply(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_session_loan(&s), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&callback), &opts) < 0) { printf("Unable to send query.\n"); return -1; } z_condvar_wait(z_condvar_loan_mut(&cond), z_mutex_loan_mut(&mutex)); z_mutex_unlock(z_mutex_loan_mut(&mutex)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_info.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include void print_zid(const z_id_t *id, void *ctx) { (void)(ctx); z_owned_string_t id_str; z_id_to_string(id, &id_str); printf(" %.*s\n", (int)z_string_len(z_string_loan(&id_str)), z_string_data(z_string_loan(&id_str))); z_string_drop(z_string_move(&id_str)); } #if Z_FEATURE_CONNECTIVITY == 1 static const char *bool_to_str(bool value) { return value ? "true" : "false"; } static void print_transport(z_loaned_transport_t *transport, void *ctx) { (void)(ctx); z_id_t zid = z_transport_zid(transport); z_owned_string_t id_str; z_id_to_string(&zid, &id_str); z_view_string_t whatami; z_whatami_to_view_string(z_transport_whatami(transport), &whatami); printf(" transport{zid=%.*s, whatami=%.*s, is_qos=%s, is_multicast=%s, is_shm=%s}\n", (int)z_string_len(z_string_loan(&id_str)), z_string_data(z_string_loan(&id_str)), (int)z_string_len(z_view_string_loan(&whatami)), z_string_data(z_view_string_loan(&whatami)), bool_to_str(z_transport_is_qos(transport)), bool_to_str(z_transport_is_multicast(transport)), bool_to_str(z_transport_is_shm(transport))); z_string_drop(z_string_move(&id_str)); } static void print_link(z_loaned_link_t *link, void *ctx) { (void)(ctx); z_id_t zid = z_link_zid(link); z_owned_string_t id_str; z_id_to_string(&zid, &id_str); z_owned_string_t src; z_owned_string_t dst; bool has_src = z_link_src(link, &src) == 0; bool has_dst = z_link_dst(link, &dst) == 0; printf(" link{zid=%.*s", (int)z_string_len(z_string_loan(&id_str)), z_string_data(z_string_loan(&id_str))); printf(", src="); if (has_src) { printf("%.*s", (int)z_string_len(z_string_loan(&src)), z_string_data(z_string_loan(&src))); } else { printf(""); } printf(", dst="); if (has_dst) { printf("%.*s", (int)z_string_len(z_string_loan(&dst)), z_string_data(z_string_loan(&dst))); } else { printf(""); } z_owned_string_t group; z_link_group(link, &group); z_owned_string_t auth_id; z_link_auth_identifier(link, &auth_id); z_owned_string_array_t interfaces; z_link_interfaces(link, &interfaces); uint8_t prio_min, prio_max; bool has_prio = z_link_priorities(link, &prio_min, &prio_max); z_reliability_t reliability; bool has_rel = z_link_reliability(link, &reliability); if (z_string_len(z_string_loan(&group)) > 0) { printf(", group=%.*s", (int)z_string_len(z_string_loan(&group)), z_string_data(z_string_loan(&group))); } if (z_string_len(z_string_loan(&auth_id)) > 0) { printf(", auth_id=%.*s", (int)z_string_len(z_string_loan(&auth_id)), z_string_data(z_string_loan(&auth_id))); } printf(", interfaces=["); size_t iface_len = z_string_array_len(z_string_array_loan(&interfaces)); for (size_t i = 0; i < iface_len; i++) { const z_loaned_string_t *iface = z_string_array_get(z_string_array_loan(&interfaces), i); if (i > 0) printf(", "); printf("%.*s", (int)z_string_len(iface), z_string_data(iface)); } printf("]"); if (has_prio) { printf(", priorities=[%u, %u]", (unsigned)prio_min, (unsigned)prio_max); } if (has_rel) { printf(", reliability=%s", (reliability == Z_RELIABILITY_RELIABLE) ? "reliable" : "best_effort"); } printf(", mtu=%u, is_streamed=%s, is_reliable=%s}\n", (unsigned)z_link_mtu(link), bool_to_str(z_link_is_streamed(link)), bool_to_str(z_link_is_reliable(link))); z_string_drop(z_string_move(&id_str)); if (has_src) { z_string_drop(z_string_move(&src)); } if (has_dst) { z_string_drop(z_string_move(&dst)); } z_string_drop(z_string_move(&group)); z_string_drop(z_string_move(&auth_id)); z_string_array_drop(z_string_array_move(&interfaces)); } static volatile bool running = true; static void handle_signal(int signo) { (void)signo; running = false; } static void transport_event_handler(z_loaned_transport_event_t *event, void *ctx) { (void)ctx; z_sample_kind_t kind = z_transport_event_kind(event); const z_loaned_transport_t *transport = z_transport_event_transport(event); printf(">> [Transport Event] %s:\n", (kind == Z_SAMPLE_KIND_PUT) ? "Opened" : "Closed"); print_transport((z_loaned_transport_t *)transport, NULL); } static void link_event_handler(z_loaned_link_event_t *event, void *ctx) { (void)ctx; z_sample_kind_t kind = z_link_event_kind(event); const z_loaned_link_t *link = z_link_event_link(event); printf(">> [Link Event] %s:\n", (kind == Z_SAMPLE_KIND_PUT) ? "Opened" : "Closed"); print_link((z_loaned_link_t *)link, NULL); } #endif int main(int argc, char **argv) { const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int opt; while ((opt = getopt(argc, argv, "e:m:l:")) != -1) { switch (opt) { case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case '?': if (optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_id_t self_id = z_info_zid(z_session_loan(&s)); printf("Own ID:"); print_zid(&self_id, NULL); printf("Routers IDs:\n"); z_owned_closure_zid_t callback; z_closure_zid(&callback, print_zid, NULL, NULL); z_info_routers_zid(z_session_loan(&s), z_closure_zid_move(&callback)); // `callback` has been `z_move`d just above, so it's safe to reuse the variable, // we'll just have to make sure we `z_move` it again to avoid mem-leaks. printf("Peers IDs:\n"); z_owned_closure_zid_t callback2; z_closure_zid(&callback2, print_zid, NULL, NULL); z_info_peers_zid(z_session_loan(&s), z_closure_zid_move(&callback2)); #if Z_FEATURE_CONNECTIVITY == 1 printf("Connected transports:\n"); z_owned_closure_transport_t transport_cb; z_closure_transport(&transport_cb, print_transport, NULL, NULL); if (z_info_transports(z_session_loan(&s), z_closure_transport_move(&transport_cb)) < 0) { printf("Unable to fetch connected transports\n"); z_session_drop(z_session_move(&s)); return -1; } printf("Connected links:\n"); z_owned_closure_link_t link_cb; z_closure_link(&link_cb, print_link, NULL, NULL); if (z_info_links(z_session_loan(&s), z_closure_link_move(&link_cb), NULL) < 0) { printf("Unable to fetch connected links\n"); z_session_drop(z_session_move(&s)); return -1; } // Register transport event listener (background, no history to avoid duplicating already-printed items) printf("Declaring transport events listener...\n"); z_owned_closure_transport_event_t te_cb; z_closure_transport_event(&te_cb, transport_event_handler, NULL, NULL); z_transport_events_listener_options_t te_opts; z_transport_events_listener_options_default(&te_opts); te_opts.history = false; if (z_declare_background_transport_events_listener(z_session_loan(&s), z_closure_transport_event_move(&te_cb), &te_opts) < 0) { printf("Unable to declare transport events listener\n"); z_session_drop(z_session_move(&s)); return -1; } // Register link event listener (non-background so we can undeclare it) printf("Declaring link events listener...\n"); z_owned_closure_link_event_t le_cb; z_closure_link_event(&le_cb, link_event_handler, NULL, NULL); z_link_events_listener_options_t le_opts; z_link_events_listener_options_default(&le_opts); le_opts.history = false; z_owned_link_events_listener_t le_listener; if (z_declare_link_events_listener(z_session_loan(&s), &le_listener, z_closure_link_event_move(&le_cb), &le_opts) < 0) { printf("Unable to declare link events listener\n"); z_session_drop(z_session_move(&s)); return -1; } signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); printf("Press CTRL-C to quit...\n"); while (running) { z_sleep_s(1); } z_link_events_listener_drop(z_link_events_listener_move(&le_listener)); #endif z_session_drop(z_session_move(&s)); return 0; } ================================================ FILE: examples/unix/c99/z_ping.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/system/platform.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 1 #define DEFAULT_PKT_SIZE 8 #define DEFAULT_PING_NB 100 #define DEFAULT_WARMUP_MS 1000 static z_owned_condvar_t cond; static z_owned_mutex_t mutex; void callback(z_loaned_sample_t* sample, void* context) { (void)sample; (void)context; z_condvar_signal(z_condvar_loan_mut(&cond)); } void drop(void* context) { (void)context; z_condvar_drop(z_condvar_move(&cond)); } struct args_t { unsigned int size; // -s unsigned int number_of_pings; // -n unsigned int warmup_ms; // -w uint8_t help_requested; // -h }; struct args_t parse_args(int argc, char** argv); int main(int argc, char** argv) { struct args_t args = parse_args(argc, argv); if (args.help_requested) { printf( "\ -n (optional, int, default=%d): the number of pings to be attempted\n\ -s (optional, int, default=%d): the size of the payload embedded in the ping and repeated by the pong\n\ -w (optional, int, default=%d): the warmup time in ms during which pings will be emitted but not measured\n\ -c (optional, string): the path to a configuration file for the session. If this option isn't passed, the default configuration will be used.\n\ ", DEFAULT_PKT_SIZE, DEFAULT_PING_NB, DEFAULT_WARMUP_MS); return 1; } z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); z_owned_session_t session; if (z_open(&session, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_owned_publisher_t pub; if (z_declare_publisher(z_session_loan(&session), &pub, z_view_keyexpr_loan(&ping), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_closure_sample_t respond; z_closure_sample(&respond, callback, drop, NULL); z_owned_subscriber_t sub; if (z_declare_subscriber(z_session_loan(&session), &sub, z_view_keyexpr_loan(&pong), z_closure_sample_move(&respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } uint8_t* data = (uint8_t*)z_malloc(args.size); for (unsigned int i = 0; i < args.size; i++) { data[i] = (uint8_t)(i % 10); } z_mutex_lock(z_mutex_loan_mut(&mutex)); if (args.warmup_ms) { printf("Warming up for %dms...\n", args.warmup_ms); z_clock_t warmup_start = z_clock_now(); unsigned long elapsed_us = 0; while (elapsed_us < args.warmup_ms * 1000) { // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, args.size, NULL, NULL); z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL); z_condvar_wait(z_condvar_loan_mut(&cond), z_mutex_loan_mut(&mutex)); elapsed_us = z_clock_elapsed_us(&warmup_start); } } unsigned long* results = (unsigned long*)z_malloc(sizeof(unsigned long) * args.number_of_pings); for (unsigned int i = 0; i < args.number_of_pings; i++) { z_clock_t measure_start = z_clock_now(); // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, args.size, NULL, NULL); z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL); z_condvar_wait(z_condvar_loan_mut(&cond), z_mutex_loan_mut(&mutex)); results[i] = z_clock_elapsed_us(&measure_start); } for (unsigned int i = 0; i < args.number_of_pings; i++) { printf("%d bytes: seq=%d rtt=%luµs, lat=%luµs\n", args.size, i, results[i], results[i] / 2); } z_mutex_unlock(z_mutex_loan_mut(&mutex)); z_free(results); z_free(data); z_subscriber_drop(z_subscriber_move(&sub)); z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&session)); } char* getopt(int argc, char** argv, char option) { for (int i = 0; i < argc; i++) { size_t len = strlen(argv[i]); if (len >= 2 && argv[i][0] == '-' && argv[i][1] == option) { if (len > 2 && argv[i][2] == '=') { return argv[i] + 3; } else if (i + 1 < argc) { return argv[i + 1]; } } } return NULL; } struct args_t parse_args(int argc, char** argv) { for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "-h") == 0) { return (struct args_t){.help_requested = 1}; } } char* arg = getopt(argc, argv, 's'); unsigned int size = DEFAULT_PKT_SIZE; if (arg) { size = (unsigned int)atoi(arg); } arg = getopt(argc, argv, 'n'); unsigned int number_of_pings = DEFAULT_PING_NB; if (arg) { number_of_pings = (unsigned int)atoi(arg); } arg = getopt(argc, argv, 'w'); unsigned int warmup_ms = DEFAULT_WARMUP_MS; if (arg) { warmup_ms = (unsigned int)atoi(arg); } return (struct args_t){ .help_requested = 0, .size = size, .number_of_pings = number_of_pings, .warmup_ms = warmup_ms, }; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION or " "Z_FEATURE_MULTI_THREAD but this example requires them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_pong.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "stdio.h" #include "zenoh-pico.h" #include "zenoh-pico/api/primitives.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 void callback(z_loaned_sample_t* sample, void* context) { const z_loaned_publisher_t* pub = z_publisher_loan((z_owned_publisher_t*)context); z_owned_bytes_t payload; z_bytes_clone(&payload, z_sample_payload(sample)); z_publisher_put(pub, z_bytes_move(&payload), NULL); } void drop(void* context) { z_owned_publisher_t* pub = (z_owned_publisher_t*)context; z_publisher_drop(z_publisher_move(pub)); // A note on lifetimes: // here, `sub` takes ownership of `pub` and will drop it before returning from its own `drop`, // which makes passing a pointer to the stack safe as long as `sub` is dropped in a scope where `pub` is still // valid. } int main(int argc, char** argv) { (void)argc; (void)argv; z_owned_config_t config; z_config_default(&config); z_owned_session_t session; if (z_open(&session, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_publisher_t pub; if (z_declare_publisher(z_session_loan(&session), &pub, z_view_keyexpr_loan(&pong), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_owned_closure_sample_t respond; z_closure_sample(&respond, callback, drop, (void*)(&pub)); if (z_declare_background_subscriber(z_session_loan(&session), z_view_keyexpr_loan(&ping), z_closure_sample_move(&respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } while (getchar() != 'q') { } z_session_drop(z_session_move(&session)); } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION but this example " "requires them.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { const char *keyexpr = "demo/example/zenoh-pico-pub"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int n = 2147483647; // max int value by default int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'v': value = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case 'n': n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_session_loan(&s), &pub, z_view_keyexpr_loan(&ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } printf("Press CTRL-C to quit...\n"); char *buf = (char *)malloc(256); for (int idx = 0; idx < n; ++idx) { z_sleep_s(1); snprintf(buf, 256, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL); } // Clean up z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_pub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 0 int main(int argc, char **argv) { const char *keyexpr = "demo/example/zenoh-pico-pub"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int n = 2147483647; // max int value by default int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:n:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'v': value = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case 'n': n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_session_loan(&s), &pub, z_view_keyexpr_loan(&ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } printf("Press CTRL-C to quit...\n"); char *buf = (char *)malloc(256); z_clock_t now = z_clock_now(); for (int idx = 0; idx < n;) { if (z_clock_elapsed_ms(&now) > 1000) { snprintf(buf, 256, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_publisher_loan(&pub), z_bytes_move(&payload), NULL); ++idx; now = z_clock_now(); } z_sleep_ms(50); zp_spin_once(z_session_loan(&s)); } z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&s)); free(buf); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_PUBLICATION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico/api/primitives.h" #if Z_FEATURE_SUBSCRIPTION == 1 int main(int argc, char **argv) { const char *keyexpr = "demo/example/**"; char *locator = NULL; size_t interval = 5000; size_t size = 3; int opt; while ((opt = getopt(argc, argv, "k:e:i:s:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'e': locator = optarg; break; case 'i': interval = (size_t)atoi(optarg); break; case 's': size = (size_t)atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'i' || optopt == 's') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); if (locator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, size); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Pulling data every %zu ms... Ring size: %zd\n", interval, size); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample); res == Z_OK; res = z_ring_handler_sample_try_recv(z_ring_handler_sample_loan(&handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_sample_loan(&sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_sample_loan(&sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); z_sample_drop(z_sample_move(&sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", interval); z_sleep_ms(interval); } else { break; } } z_subscriber_drop(z_subscriber_move(&sub)); z_ring_handler_sample_drop(z_ring_handler_sample_move(&handler)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_put.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { const char *keyexpr = "demo/example/zenoh-pico-put"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int opt; while ((opt = getopt(argc, argv, "k:v:e:m:l:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'v': value = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case '?': if (optopt == 'k' || optopt == 'v' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring key expression '%s'...\n", keyexpr); z_view_keyexpr_t vke; if (z_view_keyexpr_from_str(&vke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_owned_keyexpr_t ke; if (z_declare_keyexpr(z_session_loan(&s), &ke, z_view_keyexpr_loan(&vke)) < 0) { printf("Unable to declare key expression!\n"); z_session_drop(z_session_move(&s)); return -1; } // Create payload z_owned_bytes_t payload; z_bytes_from_static_str(&payload, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, value); if (z_put(z_session_loan(&s), z_keyexpr_loan(&ke), z_bytes_move(&payload), NULL) < 0) { printf("Oh no! Put has failed...\n"); } // Clean up z_undeclare_keyexpr(z_session_loan(&s), z_keyexpr_move(&ke)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #if Z_FEATURE_QUERYABLE == 1 const char *keyexpr = "demo/example/zenoh-pico-queryable"; const char *value = "Queryable from Pico!"; void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_view_string_loan(¶ms)), z_string_data(z_view_string_loan(¶ms))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_string_loan(&payload_string)) > 1) { printf(" with value '%.*s'\n", (int)z_string_len(z_string_loan(&payload_string)), z_string_data(z_string_loan(&payload_string))); } z_string_drop(z_string_move(&payload_string)); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply(query, z_query_keyexpr(query), z_bytes_move(&reply_payload), NULL); } int main(int argc, char **argv) { const char *mode = "client"; char *clocator = NULL; const char *llocator = NULL; int opt; while ((opt = getopt(argc, argv, "k:e:m:v:l:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'v': value = optarg; break; case 'm': mode = optarg; break; case 'e': clocator = optarg; break; case 'l': llocator = optarg; break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'v' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_owned_closure_query_t callback; z_closure_query(&callback, query_handler, NULL, NULL); printf("Creating Queryable on '%s'...\n", keyexpr); z_owned_queryable_t qable; if (z_declare_queryable(z_session_loan(&s), &qable, z_view_keyexpr_loan(&ke), z_closure_query_move(&callback), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { sleep(1); } z_queryable_drop(z_queryable_move(&qable)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_view_string_loan(&s)), z_string_data(z_view_string_loan(&s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } int main(int argc, char **argv) { (void)(argc); (void)(argv); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure_hello(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_config_move(&config), z_closure_hello_move(&closure), NULL); sleep(1); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 void data_handler(z_loaned_sample_t *sample, void *arg) { (void)(arg); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); } int main(int argc, char **argv) { const char *keyexpr = "demo/example/**"; const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int opt; while ((opt = getopt(argc, argv, "k:e:m:l:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_owned_closure_sample_t callback; z_closure_sample(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { sleep(1); } z_subscriber_drop(z_subscriber_move(&sub)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/unix/c99/z_sub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 0 static int msg_nb = 0; void data_handler(z_loaned_sample_t *sample, void *arg) { (void)(arg); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr)), (int)z_string_len(z_string_loan(&value)), z_string_data(z_string_loan(&value))); z_string_drop(z_string_move(&value)); msg_nb++; } int main(int argc, char **argv) { const char *keyexpr = "demo/example/**"; const char *mode = "client"; char *clocator = NULL; char *llocator = NULL; int n = 2147483647; // max int value by default int opt; while ((opt = getopt(argc, argv, "k:e:m:l:n:")) != -1) { switch (opt) { case 'k': keyexpr = optarg; break; case 'e': clocator = optarg; break; case 'm': mode = optarg; break; case 'l': llocator = optarg; break; case 'n': n = atoi(optarg); break; case '?': if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') { fprintf(stderr, "Option -%c requires an argument.\n", optopt); } else { fprintf(stderr, "Unknown option `-%c'.\n", optopt); } return 1; default: return -1; } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_MODE_KEY, mode); if (clocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_CONNECT_KEY, clocator); } if (llocator != NULL) { zp_config_insert(z_config_loan_mut(&config), Z_CONFIG_LISTEN_KEY, llocator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_config_move(&config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_owned_closure_sample_t callback; z_closure_sample(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_session_loan(&s), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (msg_nb < n) { z_sleep_ms(50); zp_spin_once(z_session_loan(&s)); } z_subscriber_drop(z_subscriber_move(&sub)); z_session_drop(z_session_move(&s)); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_SUBSCRIPTION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 z_owned_condvar_t cond; z_owned_mutex_t mutex; void reply_dropper(void *ctx) { (void)(ctx); printf(">> Received query final notification\n"); z_condvar_signal(z_loan_mut(cond)); z_drop(z_move(cond)); } void reply_handler(z_loaned_reply_t *reply, void *ctx) { (void)(ctx); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(">> Received an error\n"); } } int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/**"; const char *locator = NULL; const char *value = NULL; z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_mutex_lock(z_loan_mut(mutex)); printf("Sending Query '%s'...\n", keyexpr); z_get_options_t opts; z_get_options_default(&opts); // Value encoding z_owned_bytes_t payload; if (value != NULL) { z_bytes_from_static_str(&payload, value); opts.payload = z_move(payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return -1; } z_condvar_wait(z_loan_mut(cond), z_loan_mut(mutex)); z_mutex_unlock(z_loan_mut(mutex)); z_drop(z_move(s)); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires " "them.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_info.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include void print_zid(const z_id_t *id, void *ctx) { (void)(ctx); z_owned_string_t id_str; z_id_to_string(id, &id_str); printf(" %.*s\n", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str))); z_drop(z_move(id_str)); } int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_id_t self_id = z_info_zid(z_loan(s)); printf("Own ID:"); print_zid(&self_id, NULL); printf("Routers IDs:\n"); z_owned_closure_zid_t callback; z_closure(&callback, print_zid, NULL, NULL); z_info_routers_zid(z_loan(s), z_move(callback)); // `callback` has been `z_move`d just above, so it's safe to reuse the variable, // we'll just have to make sure we `z_move` it again to avoid mem-leaks. printf("Peers IDs:\n"); z_owned_closure_zid_t callback2; z_closure(&callback2, print_zid, NULL, NULL); z_info_peers_zid(z_loan(s), z_move(callback2)); z_drop(z_move(s)); } ================================================ FILE: examples/windows/z_ping.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/system/platform.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 #define DEFAULT_PKT_SIZE 8 #define DEFAULT_PING_NB 100 #define DEFAULT_WARMUP_MS 1000 static z_owned_condvar_t cond; static z_owned_mutex_t mutex; void callback(z_loaned_sample_t* sample, void* context) { (void)sample; (void)context; z_condvar_signal(z_loan_mut(cond)); } void drop(void* context) { (void)context; z_drop(z_move(cond)); } struct args_t { unsigned int size; // -s unsigned int number_of_pings; // -n unsigned int warmup_ms; // -w uint8_t help_requested; // -h }; struct args_t parse_args(int argc, char** argv); int main(int argc, char** argv) { struct args_t args = parse_args(argc, argv); if (args.help_requested) { printf( "\ -n (optional, int, default=%d): the number of pings to be attempted\n\ -s (optional, int, default=%d): the size of the payload embedded in the ping and repeated by the pong\n\ -w (optional, int, default=%d): the warmup time in ms during which pings will be emitted but not measured\n\ -c (optional, string): the path to a configuration file for the session. If this option isn't passed, the default configuration will be used.\n\ ", DEFAULT_PKT_SIZE, DEFAULT_PING_NB, DEFAULT_WARMUP_MS); return 1; } z_mutex_init(&mutex); z_condvar_init(&cond); z_owned_config_t config; z_config_default(&config); z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(session), &pub, z_loan(ping), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_closure_sample_t respond; z_closure(&respond, callback, drop, NULL); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(session), &sub, z_loan(pong), z_move(respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } uint8_t* data = (uint8_t*)z_malloc(args.size); for (unsigned int i = 0; i < args.size; i++) { data[i] = i % 10; } z_mutex_lock(z_loan_mut(mutex)); if (args.warmup_ms) { printf("Warming up for %dms...\n", args.warmup_ms); z_clock_t warmup_start = z_clock_now(); unsigned long elapsed_us = 0; while (elapsed_us < args.warmup_ms * 1000) { // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, args.size, NULL, NULL); z_publisher_put(z_loan(pub), z_move(payload), NULL); z_condvar_wait(z_loan_mut(cond), z_loan_mut(mutex)); elapsed_us = z_clock_elapsed_us(&warmup_start); } } unsigned long* results = (unsigned long*)z_malloc(sizeof(unsigned long) * args.number_of_pings); for (unsigned int i = 0; i < args.number_of_pings; i++) { z_clock_t measure_start = z_clock_now(); // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, args.size, NULL, NULL); z_publisher_put(z_loan(pub), z_move(payload), NULL); z_condvar_wait(z_loan_mut(cond), z_loan_mut(mutex)); results[i] = z_clock_elapsed_us(&measure_start); } for (unsigned int i = 0; i < args.number_of_pings; i++) { printf("%d bytes: seq=%d rtt=%luus, lat=%luus\n", args.size, i, results[i], results[i] / 2); } z_mutex_unlock(z_loan_mut(mutex)); z_free(results); z_free(data); z_drop(z_move(pub)); z_drop(z_move(sub)); z_drop(z_move(session)); } char* getopt(int argc, char** argv, char option) { for (int i = 0; i < argc; i++) { size_t len = strlen(argv[i]); if (len >= 2 && argv[i][0] == '-' && argv[i][1] == option) { if (len > 2 && argv[i][2] == '=') { return argv[i] + 3; } else if (i + 1 < argc) { return argv[i + 1]; } } } return NULL; } struct args_t parse_args(int argc, char** argv) { for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "-h") == 0) { return (struct args_t){.help_requested = 1}; } } char* arg = getopt(argc, argv, 's'); unsigned int size = DEFAULT_PKT_SIZE; if (arg) { size = atoi(arg); } arg = getopt(argc, argv, 'n'); unsigned int number_of_pings = DEFAULT_PING_NB; if (arg) { number_of_pings = atoi(arg); } arg = getopt(argc, argv, 'w'); unsigned int warmup_ms = DEFAULT_WARMUP_MS; if (arg) { warmup_ms = atoi(arg); } return (struct args_t){ .help_requested = 0, .size = size, .number_of_pings = number_of_pings, .warmup_ms = warmup_ms, }; } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION but this example " "requires them.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_pong.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "stdio.h" #include "zenoh-pico.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 void callback(z_loaned_sample_t* sample, void* context) { const z_loaned_publisher_t* pub = z_loan(*(z_owned_publisher_t*)context); z_owned_bytes_t payload; z_bytes_clone(&payload, z_sample_payload(sample)); z_publisher_put(pub, z_move(payload), NULL); } void drop(void* context) { z_owned_publisher_t* pub = (z_owned_publisher_t*)context; z_drop(z_move(*pub)); // A note on lifetimes: // here, `sub` takes ownership of `pub` and will drop it before returning from its own `drop`, // which makes passing a pointer to the stack safe as long as `sub` is dropped in a scope where `pub` is still // valid. } int main(int argc, char** argv) { (void)argc; (void)argv; z_owned_config_t config; z_config_default(&config); z_owned_session_t session; if (z_open(&session, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t pong; z_view_keyexpr_from_str_unchecked(&pong, "test/pong"); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(session), &pub, z_loan(pong), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } z_view_keyexpr_t ping; z_view_keyexpr_from_str_unchecked(&ping, "test/ping"); z_owned_closure_sample_t respond; z_closure(&respond, callback, drop, (void*)(&pub)); if (z_declare_background_subscriber(z_loan(session), z_loan(ping), z_move(respond), NULL) < 0) { printf("Unable to declare subscriber for key expression.\n"); return -1; } while (getchar() != 'q') { } z_drop(z_move(session)); } #else int main(void) { printf( "ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION or Z_FEATURE_PUBLICATION but this example " "requires them.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/zenoh-pico-pub"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } printf("Press CTRL-C to quit...\n"); char *buf = (char *)malloc(256); for (int idx = 0; 1; ++idx) { z_sleep_s(1); snprintf(buf, 256, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_from_str(&payload, buf, NULL, NULL); z_publisher_put(z_loan(pub), z_move(payload), NULL); } // Clean-up z_drop(z_move(pub)); z_drop(z_move(s)); free(buf); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_pub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #define N 2147483647 // max int value by default #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_MULTI_THREAD == 0 int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/zenoh-pico-pub"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring publisher for '%s'...\n", keyexpr); z_owned_publisher_t pub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } printf("Press CTRL-C to quit...\n"); char *buf = (char *)malloc(256); z_clock_t now = z_clock_now(); for (int idx = 0; idx < N;) { if (z_clock_elapsed_ms(&now) > 1000) { snprintf(buf, 256, "[%4d] %s", idx, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); ++idx; now = z_clock_now(); } z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(pub)); z_drop(z_move(s)); free(buf); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_PUBLICATION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/**"; char *locator = NULL; size_t interval = 5000; size_t size = 3; z_owned_config_t config; z_config_default(&config); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, size); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Pulling data every %zu ms... Ring size: %zd\n", interval, size); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", interval); z_sleep_ms(interval); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_put.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/zenoh-pico-put"; const char *value = "Pub from Pico!"; const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("Declaring key expression '%s'...\n", keyexpr); z_view_keyexpr_t vke; if (z_view_keyexpr_from_str(&vke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } z_owned_keyexpr_t ke; if (z_declare_keyexpr(z_loan(s), &ke, z_loan(vke)) < 0) { printf("Unable to declare key expression!\n"); z_drop(z_move(s)); return -1; } // Create payload z_owned_bytes_t payload; z_bytes_from_static_str(&payload, value); printf("Putting Data ('%s': '%s')...\n", keyexpr, value); if (z_put(z_loan(s), z_loan(ke), z_move(payload), NULL) < 0) { printf("Oh no! Put has failed...\n"); } // Clean up z_undeclare_keyexpr(z_loan(s), z_move(ke)); z_drop(z_move(s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_QUERYABLE == 1 const char *keyexpr = "demo/example/zenoh-pico-queryable"; const char *value = "Queryable from Pico!"; void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); } int main(int argc, char **argv) { (void)(argc); (void)(argv); char *locator = NULL; z_owned_config_t config; z_config_default(&config); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } printf("Creating Queryable on '%s'...\n", keyexpr); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create queryable.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { Sleep(1); } z_drop(z_move(qable)); z_drop(z_move(s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } int main(int argc, char **argv) { (void)(argc); (void)(argv); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_move(config), z_move(closure), NULL); Sleep(1); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); } int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/**"; const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (1) { Sleep(1); } z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/windows/z_sub_st.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #define N 2147483647 // max int value by default int msg_nb = 0; #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_MULTI_THREAD == 0 void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); msg_nb++; } int main(int argc, char **argv) { (void)(argc); (void)(argv); const char *keyexpr = "demo/example/**"; const char *mode = "client"; char *locator = NULL; z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); if (locator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, locator); } printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); printf("Declaring Subscriber on '%s'...\n", keyexpr); z_owned_subscriber_t sub; z_view_keyexpr_t ke; if (z_view_keyexpr_from_str(&ke, keyexpr) < 0) { printf("%s is not a valid key expression\n", keyexpr); return -1; } if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Press CTRL-C to quit...\n"); while (msg_nb < N) { z_sleep_ms(50); zp_spin_once(z_loan(s)); } z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } #else int main(void) { printf( "ERROR: Zenoh pico must be compiled with Z_FEATURE_SUBSCRIPTION = 1 and Z_FEATURE_MULTI_THREAD = 0 to run this " "example.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_get.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #if Z_FEATURE_QUERY == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" #define VALUE "" void reply_dropper(void *ctx) { printf(" >> Received query final notification\n"); } void reply_handler(z_loaned_reply_t *oreply, void *ctx) { if (z_reply_is_ok(oreply)) { const z_loaned_sample_t *sample = z_reply_ok(oreply); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(" >> Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } else { printf(" >> Received an error\n"); } } int main(int argc, char **argv) { sleep(5); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("OK\n"); while (1) { sleep(5); printf("Sending Query '%s'...\n", KEYEXPR); z_get_options_t opts; z_get_options_default(&opts); opts.target = Z_QUERY_TARGET_ALL; // Value encoding z_owned_bytes_t payload; if (strcmp(VALUE, "") != 0) { z_bytes_from_static_str(&payload, VALUE); opts.payload = z_move(payload); } z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, reply_dropper, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_get(z_loan(s), z_loan(ke), "", z_move(callback), &opts) < 0) { printf("Unable to send query.\n"); return -1; } } printf("Closing Zenoh Session..."); z_drop(z_move(s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERY but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_pub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-pub" #define VALUE "[STSTM32]{nucleo-F767ZI} Pub from Zenoh-Pico!" #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { sleep(5); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("OK\n"); printf("Declaring publisher for '%s'...", KEYEXPR); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_publisher_t pub; if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); return -1; } printf("OK\n"); char buf[256]; for (int idx = 0; 1; ++idx) { sleep(1); sprintf(buf, "[%4d] %s", idx, VALUE); printf("Putting Data ('%s': '%s')...\n", KEYEXPR, buf); // Create payload z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); z_publisher_put(z_loan(pub), z_move(payload), NULL); } printf("Closing Zenoh Session..."); z_drop(z_move(pub)); z_drop(z_move(s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_pull.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" const size_t INTERVAL = 5000; const size_t SIZE = 3; int main(int argc, char **argv) { sleep(5); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("OK\n"); printf("Declaring Subscriber on '%s'...\n", KEYEXPR); z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, SIZE); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(closure), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("Pulling data every %zu ms... Ring size: %zd\n", INTERVAL, SIZE); z_owned_sample_t sample; while (true) { z_result_t res; for (res = z_try_recv(z_loan(handler), &sample); res == Z_OK; res = z_try_recv(z_loan(handler), &sample)) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(z_loan(sample)), &value); printf(">> [Subscriber] Pulled ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); z_drop(z_move(sample)); } if (res == Z_CHANNEL_NODATA) { printf(">> [Subscriber] Nothing to pull... sleep for %zu ms\n", INTERVAL); z_sleep_ms(INTERVAL); } else { break; } } z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #if Z_FEATURE_QUERYABLE == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/zenoh-pico-queryable" #define VALUE "[STSTM32]{nucleo-F767ZI} Queryable from Zenoh-Pico!" void query_handler(z_loaned_query_t *query, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable handler] Received Query '%.*s%.*s'\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Process value z_owned_string_t payload_string; z_bytes_to_string(z_query_payload(query), &payload_string); if (z_string_len(z_loan(payload_string)) > 0) { printf(" with value '%.*s'\n", (int)z_string_len(z_loan(payload_string)), z_string_data(z_loan(payload_string))); } z_drop(z_move(payload_string)); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, VALUE); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); } int main(int argc, char **argv) { sleep(5); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("OK\n"); // Declare Zenoh queryable printf("Declaring Queryable on %s...", KEYEXPR); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, NULL); z_owned_queryable_t qable; z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); if (z_declare_queryable(z_loan(s), &qable, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare queryable.\n"); return -1; } printf("OK\n"); printf("Zenoh setup finished!\n"); while (1) { sleep(1); } printf("Closing Zenoh Session..."); z_drop(z_move(qable)); z_drop(z_move(s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_QUERYABLE but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #if Z_FEATURE_SCOUTING == 1 void fprintzid(FILE *stream, z_id_t zid) { unsigned int zidlen = _z_id_len(zid); if (zidlen == 0) { fprintf(stream, "None"); } else { fprintf(stream, "Some("); for (unsigned int i = 0; i < zidlen; i++) { fprintf(stream, "%02X", (int)zid.id[i]); } fprintf(stream, ")"); } } void fprintwhatami(FILE *stream, z_whatami_t whatami) { z_view_string_t s; z_whatami_to_view_string(whatami, &s); fprintf(stream, "\"%.*s\"", (int)z_string_len(z_loan(s)), z_string_data(z_loan(s))); } void fprintlocators(FILE *stream, const z_loaned_string_array_t *locs) { fprintf(stream, "["); for (unsigned int i = 0; i < z_string_array_len(locs); i++) { fprintf(stream, "\""); const z_loaned_string_t *str = z_string_array_get(locs, i); fprintf(stream, "%.*s", (int)z_string_len(str), z_string_data(str)); fprintf(stream, "\""); if (i < z_string_array_len(locs) - 1) { fprintf(stream, ", "); } } fprintf(stream, "]"); } void fprinthello(FILE *stream, const z_loaned_hello_t *hello) { fprintf(stream, "Hello { zid: "); fprintzid(stream, z_hello_zid(hello)); fprintf(stream, ", whatami: "); fprintwhatami(stream, z_hello_whatami(hello)); fprintf(stream, ", locators: "); fprintlocators(stream, zp_hello_locators(hello)); fprintf(stream, " }"); } void callback(z_loaned_hello_t *hello, void *context) { fprinthello(stdout, hello); fprintf(stdout, "\n"); (*(int *)context)++; } void drop(void *context) { int count = *(int *)context; free(context); if (!count) { printf("Did not find any zenoh process.\n"); } else { printf("Dropping scout results.\n"); } } int main(void) { sleep(5); int *context = (int *)malloc(sizeof(int)); *context = 0; z_owned_config_t config; z_config_default(&config); z_owned_closure_hello_t closure; z_closure_hello(&closure, callback, drop, context); printf("Scouting...\n"); z_scout(z_config_move(&config), z_closure_hello_move(&closure), NULL); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SCOUTING but this example requires it.\n"); return -2; } #endif ================================================ FILE: examples/zephyr/z_sub.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 #define CLIENT_OR_PEER 0 // 0: Client mode; 1: Peer mode #if CLIENT_OR_PEER == 0 #define MODE "client" #define LOCATOR "" // If empty, it will scout #elif CLIENT_OR_PEER == 1 #define MODE "peer" #define LOCATOR "udp/224.0.0.225:7447#iface=en0" #else #error "Unknown Zenoh operation mode. Check CLIENT_OR_PEER value." #endif #define KEYEXPR "demo/example/**" void data_handler(z_loaned_sample_t *sample, void *arg) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(" >> [Subscriber handler] Received ('%.*s': '%.*s')\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); } int main(int argc, char **argv) { sleep(5); // Initialize Zenoh Session and other parameters z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, MODE); if (strcmp(LOCATOR, "") != 0) { if (strcmp(MODE, "client") == 0) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, LOCATOR); } else { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, LOCATOR); } } // Open Zenoh session printf("Opening Zenoh Session..."); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } printf("OK\n"); printf("Declaring Subscriber on '%s'...", KEYEXPR); z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); z_view_keyexpr_t ke; z_view_keyexpr_from_str_unchecked(&ke, KEYEXPR); z_owned_subscriber_t sub; if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } printf("OK!\n"); while (1) { sleep(1); } printf("Closing Zenoh Session..."); z_drop(z_move(sub)); z_drop(z_move(s)); printf("OK!\n"); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this example requires it.\n"); return -2; } #endif ================================================ FILE: extra_script.py ================================================ # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # import os import subprocess Import('env', 'projenv') SRC_FILTER = [] CPPDEFINES = [] ZP_PLATFORM = None BASE_SRC_FILTER = [ "+<*>", "-", "-", "-", "+", "+", ] def _platform_src_filter(system_dir): return BASE_SRC_FILTER + [f"+<{system_dir}/>"] FRAMEWORK = env.get("PIOFRAMEWORK")[0] PLATFORM = env.get("PIOPLATFORM") BOARD = env.get("PIOENV") ZENOH_GENERIC = env.get("ZENOH_GENERIC", "0") if ZENOH_GENERIC == "1": FRAMEWORK = 'generic' PLATFORM = 'generic' BOARD = 'generic' if FRAMEWORK == 'zephyr': SRC_FILTER = _platform_src_filter("system/zephyr") ZP_PLATFORM = "zephyr" CPPDEFINES = [ "ZENOH_ZEPHYR", ] elif FRAMEWORK == 'arduino': PLATFORM = env.get("PIOPLATFORM") if PLATFORM == 'espressif32': SRC_FILTER = _platform_src_filter("system/arduino/esp32") ZP_PLATFORM = "arduino_esp32" CPPDEFINES = [ "ZENOH_ARDUINO_ESP32", "ZENOH_COMPILER_GCC", "ZENOH_C_STANDARD=99", ] if PLATFORM == 'ststm32': BOARD = env.get("PIOENV") if BOARD == 'opencr': SRC_FILTER = _platform_src_filter("system/arduino/opencr") ZP_PLATFORM = "opencr" CPPDEFINES = [ "ZENOH_ARDUINO_OPENCR", "ZENOH_C_STANDARD=99", "Z_FEATURE_MULTI_THREAD=0", ] elif FRAMEWORK == 'espidf': SRC_FILTER = _platform_src_filter("system/espidf") ZP_PLATFORM = "espidf" CPPDEFINES = [ "ZENOH_ESPIDF", ] elif FRAMEWORK == 'mbed': SRC_FILTER = _platform_src_filter("system/mbed") ZP_PLATFORM = "mbed" CPPDEFINES = [ "ZENOH_MBED", "ZENOH_C_STANDARD=99", ] elif FRAMEWORK == 'generic': SRC_FILTER = BASE_SRC_FILTER CPPDEFINES = ["ZENOH_GENERIC"] env.Append(SRC_FILTER=SRC_FILTER) env.Append(CPPDEFINES=CPPDEFINES) # pass flags to the main project environment projenv.Append(CPPDEFINES=CPPDEFINES) # pass flags to a global build environment (for all libraries, etc) global_env = DefaultEnvironment() global_env.Append(CPPDEFINES=CPPDEFINES) # Run CMake for zenoh-pico # Default embedded buffer values default_args = ["-DFRAG_MAX_SIZE=4096", "-DBATCH_UNICAST_SIZE=2048", "-DBATCH_MULTICAST_SIZE=2048"] # Get the additional CMake arguments from the environment cmake_extra_args = env.BoardConfig().get("build.cmake_extra_args", "") cmake_extra_args_list = cmake_extra_args.split() def _has_cmake_arg(name): prefix = f"{name}=" return any(arg.startswith(prefix) for arg in cmake_extra_args_list) if ( ZP_PLATFORM is not None and not _has_cmake_arg("-DZP_PLATFORM") ): cmake_extra_args_list.append(f"-DZP_PLATFORM={ZP_PLATFORM}") # Add default value if needed for default in default_args: arg_name = default.split('=')[0] if not any(arg.startswith(arg_name) for arg in cmake_extra_args_list): cmake_extra_args_list.append(default) # Define the source and binary directories source_dir = os.getcwd() build_dir = os.path.join(source_dir, "build") os.makedirs(build_dir, exist_ok=True) generated_include_dir = os.path.join(build_dir, "include") for build_env in (env, projenv, global_env): # pylint: disable=undefined-variable build_env.Prepend(CPPPATH=[generated_include_dir]) build_env.Prepend(CCFLAGS=[f"-I{generated_include_dir}"]) # Run the CMake command with the source and binary directories print("Run command: cmake", source_dir, cmake_extra_args_list) subprocess.run(["cmake", source_dir] + cmake_extra_args_list, cwd=build_dir, check=True) ================================================ FILE: include/zenoh-pico/api/admin_space.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_API_ADMIN_SPACE_H #define ZENOH_PICO_API_ADMIN_SPACE_H #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/list.h" #ifdef __cplusplus extern "C" { #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADMIN_SPACE == 1 /** * Starts the admin space for a session. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to start the admin space on. * * Return: * ``0`` if start operation is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t zp_start_admin_space(z_loaned_session_t *zs); /** * Stops the admin space for a session. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to stop the admin space on. * * Return: * ``0`` if stop operation is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t zp_stop_admin_space(z_loaned_session_t *zs); typedef struct { z_owned_keyexpr_t ke; z_owned_bytes_t payload; } _ze_admin_space_reply_t; void _ze_admin_space_reply_clear(_ze_admin_space_reply_t *reply); _Z_ELEM_DEFINE(_ze_admin_space_reply, _ze_admin_space_reply_t, _z_noop_size, _ze_admin_space_reply_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_LIST_DEFINE(_ze_admin_space_reply, _ze_admin_space_reply_t) #endif // Z_FEATURE_ADMIN_SPACE == 1 #endif // Z_FEATURE_UNSTABLE_API #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_API_ADMIN_SPACE_H */ ================================================ FILE: include/zenoh-pico/api/advanced_publisher.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_ADVANCED_PUBLISHER_H #define INCLUDE_ZENOH_PICO_API_ADVANCED_PUBLISHER_H #include #include "olv_macros.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/advanced_cache.h" #include "zenoh-pico/collections/atomic.h" #include "zenoh-pico/collections/seqnumber.h" #include "zenoh-pico/runtime/runtime.h" #ifdef __cplusplus extern "C" { #endif typedef enum { _ZE_ADVANCED_PUBLISHER_SEQUENCING_NONE = 0, _ZE_ADVANCED_PUBLISHER_SEQUENCING_TIMESTAMP = 1, _ZE_ADVANCED_PUBLISHER_SEQUENCING_SEQUENCE_NUMBER = 2 } _ze_advanced_publisher_sequencing_t; /** * Whatami values, defined as a bitmask. * * Enumerators: * ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE: Disable heartbeat-based last sample miss detection. * ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_PERIODIC: Allow last sample miss detection through periodic * heartbeat. Periodically send the last published Sample's sequence number to allow last sample recovery. * ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_SPORADIC: Allow last sample miss detection through sporadic * heartbeat. Each period, the last published Sample's sequence number is sent with * `Z_CONGESTION_CONTROL_DROP` but only if it changed since last period. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef enum { ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE, ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_PERIODIC, ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_SPORADIC, } ze_advanced_publisher_heartbeat_mode_t; #define ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_DEFAULT ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE typedef struct _ze_advanced_publisher_state_t { _z_atomic_size_t _seqnumber; ze_advanced_publisher_heartbeat_mode_t _heartbeat_mode; _z_session_weak_t _zn; z_owned_publisher_t _publisher; _z_fut_handle_t _state_publisher_task_handle; uint64_t _heartbeat_period_ms; uint32_t _last_published_sn; } _ze_advanced_publisher_state_t; void _ze_advanced_publisher_state_clear(_ze_advanced_publisher_state_t *state); _Z_REFCOUNT_DEFINE_NO_FROM_VAL(_ze_advanced_publisher_state, _ze_advanced_publisher_state) typedef struct { z_owned_publisher_t _publisher; _ze_advanced_cache_t *_cache; bool _has_liveliness; z_owned_liveliness_token_t _liveliness; _ze_advanced_publisher_sequencing_t _sequencing; _ze_advanced_publisher_state_rc_t _state; } _ze_advanced_publisher_t; _Z_OWNED_TYPE_VALUE_PREFIX(ze, _ze_advanced_publisher_t, advanced_publisher) _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(ze, advanced_publisher) #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADVANCED_PUBLICATION == 1 /**************** Advanced Publisher ****************/ /** * Represents the set of options for sample miss detection on an advanced publisher. * * Members: * bool is_enabled: Must be set to ``true``, to enable sample miss detection by adding * sequence numbers. * enum ze_advanced_publisher_heartbeat_mode_t heartbeat_mode: Allow last sample miss * detection through sporadic or periodic heartbeat. * uint64_t heartbeat_period_ms: If heartbeat_mode is not ``NONE``, the publisher will send * heartbeats with the specified period, which can be used by Advanced Subscribers for last * sample(s) miss detection (if last sample miss detection with zero query period is enabled). * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { bool is_enabled; ze_advanced_publisher_heartbeat_mode_t heartbeat_mode; uint64_t heartbeat_period_ms; } ze_advanced_publisher_sample_miss_detection_options_t; /** * Represents the set of options that can be applied to an advanced publisher, * upon its declaration via :c:func:`ze_declare_advanced_publisher`. * * Members: * z_publisher_options_t publisher_options: Base publisher options. * ze_advanced_publisher_cache_options_t cache: Publisher cache settings. * ze_advanced_publisher_sample_miss_detection_options_t sample_miss_detection: Allow * matching Subscribers to detect lost samples and optionally ask for retransmission. * Retransmission can only be done if history is enabled on subscriber side. * bool publisher_detection: Allow this publisher to be detected through liveliness. * z_loaned_keyexpr_t *publisher_detection_metadata: An optional key expression to be added * to the liveliness token key expression. It can be used to convey meta data. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { z_publisher_options_t publisher_options; ze_advanced_publisher_cache_options_t cache; ze_advanced_publisher_sample_miss_detection_options_t sample_miss_detection; bool publisher_detection; z_loaned_keyexpr_t *publisher_detection_metadata; } ze_advanced_publisher_options_t; /** * Builds a :c:type:`ze_advanced_publisher_cache_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_publisher_cache_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_publisher_cache_options_default(ze_advanced_publisher_cache_options_t *options); /** * Builds a :c:type:`ze_advanced_publisher_sample_miss_detection_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_publisher_sample_miss_detection_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_publisher_sample_miss_detection_options_default( ze_advanced_publisher_sample_miss_detection_options_t *options); /** * Builds a :c:type:`ze_advanced_publisher_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_publisher_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_publisher_options_default(ze_advanced_publisher_options_t *options); /** * Declares an advanced publisher for a given keyexpr. * * Data can be put and deleted with this advanced publisher with the help of the * :c:func:`ze_advanced_publisher_put` and :c:func:`ze_advanced_publisher_delete` functions. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the advanced publisher through. * pub: Pointer to an uninitialized :c:type:`ze_owned_advanced_publisher_t`. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the advanced publisher with. * options: Pointer to a :c:type:`ze_advanced_publisher_options_t` to configure the operation. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_declare_advanced_publisher(const z_loaned_session_t *zs, ze_owned_advanced_publisher_t *pub, const z_loaned_keyexpr_t *keyexpr, const ze_advanced_publisher_options_t *options); /** * Undeclares the advanced publisher. * * Parameters: * pub: Moved :c:type:`ze_owned_advanced_publisher_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_undeclare_advanced_publisher(ze_moved_advanced_publisher_t *pub); /** * Represents the set of options passed to the `ze_advanced_publisher_put()` function. * * Members: * z_publisher_put_options_t put_options: Base put options. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { z_publisher_put_options_t put_options; } ze_advanced_publisher_put_options_t; /** * Builds a :c:type:`ze_advanced_publisher_put_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_publisher_put_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_publisher_put_options_default(ze_advanced_publisher_put_options_t *options); /** * Represents the set of options passed to the `ze_advanced_publisher_delete()` function. * * Members: * z_publisher_delete_options_t delete_options: Base delete options. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { z_publisher_delete_options_t delete_options; } ze_advanced_publisher_delete_options_t; /** * Builds a :c:type:`ze_advanced_publisher_delete_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_publisher_delete_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_publisher_delete_options_default(ze_advanced_publisher_delete_options_t *options); /** * Puts data for the keyexpr bound to the given advanced publisher. * * Parameters: * pub: Pointer to a :c:type:`ze_loaned_advanced_publisher_t` from where to put the data. * payload: Moved :c:type:`z_owned_bytes_t` containing the data to put. * options: Pointer to a :c:type:`ze_advanced_publisher_put_options_t` to configure the operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_publisher_put(const ze_loaned_advanced_publisher_t *pub, z_moved_bytes_t *payload, const ze_advanced_publisher_put_options_t *options); /** * Deletes data from the keyexpr bound to the given advanced publisher. * * Parameters: * pub: Pointer to a :c:type:`ze_loaned_advanced_publisher_t` from where to delete the data. * options: Pointer to a :c:type:`ze_advanced_publisher_delete_options_t` to configure the delete operation. * * Return: * ``0`` if delete operation is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_publisher_delete(const ze_loaned_advanced_publisher_t *pub, const ze_advanced_publisher_delete_options_t *options); /** * Gets the keyexpr from an advanced publisher. * * Parameters: * publisher: Pointer to a :c:type:`ze_loaned_advanced_publisher_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ const z_loaned_keyexpr_t *ze_advanced_publisher_keyexpr(const ze_loaned_advanced_publisher_t *pub); /** * Gets the entity global Id from an advanced publisher. * * Parameters: * publisher: Pointer to a :c:type:`ze_loaned_advanced_publisher_t` to get the entity global Id from. * * Return: * The entity global Id wrapped as a :c:type:`z_entity_global_global_id_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_entity_global_id_t ze_advanced_publisher_id(const ze_loaned_advanced_publisher_t *pub); #if Z_FEATURE_MATCHING == 1 /** * Gets advanced publisher matching status - i.e. if there are any subscribers matching its key expression. * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_publisher_get_matching_status(const ze_loaned_advanced_publisher_t *publisher, z_matching_status_t *matching_status); /** * Constructs matching listener, registering a callback for notifying subscribers matching with a given advanced * publisher. * * Parameters: * publisher: An advanced publisher to associate with matching listener. * matching_listener: An uninitialized memory location where matching listener will be constructed. The matching * listener's callback will be automatically dropped when the publisher is dropped. * callback: A closure that will be called every time the matching status of the publisher changes (If last subscriber * disconnects or when the first subscriber connects). * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_publisher_declare_matching_listener(const ze_loaned_advanced_publisher_t *publisher, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback); /** * Declares a matching listener, registering a callback for notifying subscribers matching with a given advanced * publisher. The callback will be run in the background until the corresponding advanced publisher is dropped. * * Parameters: * publisher: An advanced publisher to associate with matching listener. * callback: A closure that will be called every time the matching status of the publisher changes (If last subscriber * disconnects or when the first subscriber connects). * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_publisher_declare_background_matching_listener(const ze_loaned_advanced_publisher_t *publisher, z_moved_closure_matching_status_t *callback); #endif // Z_FEATURE_MATCHING == 1 #endif // Z_FEATURE_ADVANCED_PUBLICATION == 1 #endif // Z_FEATURE_UNSTABLE_API #ifdef __cplusplus } #endif #endif // INCLUDE_ZENOH_PICO_API_ADVANCED_PUBLISHER_H ================================================ FILE: include/zenoh-pico/api/advanced_subscriber.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_ADVANCED_SUBSCRIBER_H #define INCLUDE_ZENOH_PICO_API_ADVANCED_SUBSCRIBER_H #include "olv_macros.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/hashmap.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/collections/sortedmap.h" #include "zenoh-pico/runtime/runtime.h" #ifdef __cplusplus extern "C" { #endif static inline size_t _z_uint32_size(const uint32_t *e) { _ZP_UNUSED(e); return sizeof(uint32_t); } static inline void _z_uint32_copy(uint32_t *dst, const uint32_t *src) { *dst = *src; } static inline int _z_uint32_cmp(const uint32_t *left, const uint32_t *right) { if (*left < *right) return -1; if (*left > *right) return 1; return 0; } _Z_ELEM_DEFINE(_z_uint32, uint32_t, _z_uint32_size, _z_noop_clear, _z_uint32_copy, _z_noop_move, _z_noop_eq, _z_uint32_cmp, _z_noop_hash) _Z_SORTEDMAP_DEFINE(_z_uint32, _z_sample, uint32_t, _z_sample_t) _Z_SORTEDMAP_DEFINE(_z_timestamp, _z_sample, _z_timestamp_t, _z_sample_t) typedef struct { _z_session_weak_t _zn; bool _has_last_delivered; uint32_t _last_delivered; uint64_t _pending_queries; _z_uint32__z_sample_sortedmap_t _pending_samples; _z_fut_handle_t _periodic_query_handle; z_owned_keyexpr_t _query_keyexpr; } _ze_advanced_subscriber_sequenced_state_t; static inline size_t _ze_advanced_subscriber_sequenced_state_size(_ze_advanced_subscriber_sequenced_state_t *s) { _ZP_UNUSED(s); return sizeof(_ze_advanced_subscriber_sequenced_state_t); } void _ze_advanced_subscriber_sequenced_state_clear(_ze_advanced_subscriber_sequenced_state_t *s); _Z_ELEM_DEFINE(_ze_advanced_subscriber_sequenced_state, _ze_advanced_subscriber_sequenced_state_t, _ze_advanced_subscriber_sequenced_state_size, _ze_advanced_subscriber_sequenced_state_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) typedef struct { bool _has_last_delivered; _z_timestamp_t _last_delivered; uint64_t _pending_queries; _z_timestamp__z_sample_sortedmap_t _pending_samples; } _ze_advanced_subscriber_timestamped_state_t; static inline size_t _ze_advanced_subscriber_timestamped_state_size(_ze_advanced_subscriber_timestamped_state_t *s) { _ZP_UNUSED(s); return sizeof(_ze_advanced_subscriber_timestamped_state_t); } void _ze_advanced_subscriber_timestamped_state_clear(_ze_advanced_subscriber_timestamped_state_t *s); _Z_ELEM_DEFINE(_ze_advanced_subscriber_timestamped_state, _ze_advanced_subscriber_timestamped_state_t, _ze_advanced_subscriber_timestamped_state_size, _ze_advanced_subscriber_timestamped_state_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_HASHMAP_DEFINE(_z_entity_global_id, _ze_advanced_subscriber_sequenced_state, _z_entity_global_id_t, _ze_advanced_subscriber_sequenced_state_t) _Z_HASHMAP_DEFINE(_z_id, _ze_advanced_subscriber_timestamped_state, z_id_t, _ze_advanced_subscriber_timestamped_state_t) static inline _ze_closure_miss_t _ze_closure_miss_null(void) { _ze_closure_miss_t miss = {0}; return miss; } static inline void _ze_closure_miss_drop(_ze_closure_miss_t *closure) { if (closure->drop != NULL) { closure->drop(closure->context); } *closure = _ze_closure_miss_null(); } static inline void _ze_closure_miss_copy(_ze_closure_miss_t *dst, const _ze_closure_miss_t *src) { *dst = *src; } static inline void _ze_closure_miss_move(_ze_closure_miss_t *dst, _ze_closure_miss_t *src) { *dst = *src; *src = _ze_closure_miss_null(); } _Z_ELEM_DEFINE(_ze_closure_miss, _ze_closure_miss_t, _z_noop_size, _ze_closure_miss_drop, _ze_closure_miss_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_ze_closure_miss, _ze_closure_miss_t) typedef struct { #if Z_FEATURE_MULTI_THREAD == 1 z_owned_mutex_t _mutex; #endif size_t _next_id; uint64_t _global_pending_queries; _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_t _sequenced_states; _z_id__ze_advanced_subscriber_timestamped_state_hashmap_t _timestamped_states; _z_session_weak_t _zn; z_owned_keyexpr_t _keyexpr; bool _retransmission; bool _has_period; uint64_t _period_ms; size_t _history_depth; uint64_t _history_age; z_query_target_t _query_target; uint64_t _query_timeout; _z_closure_sample_callback_t _callback; _z_drop_handler_t _dropper; void *_ctx; _ze_closure_miss_intmap_t _miss_handlers; bool _has_token; z_owned_liveliness_token_t _token; z_owned_cancellation_token_t _cancellation_token; bool _is_undeclaring; } _ze_advanced_subscriber_state_t; _ze_advanced_subscriber_state_t _ze_advanced_subscriber_state_null(void); void _ze_advanced_subscriber_state_clear(_ze_advanced_subscriber_state_t *state); _Z_REFCOUNT_DEFINE(_ze_advanced_subscriber_state, _ze_advanced_subscriber_state) typedef struct { z_owned_subscriber_t _subscriber; bool _has_liveliness_subscriber; z_owned_subscriber_t _liveliness_subscriber; bool _has_heartbeat_subscriber; z_owned_subscriber_t _heartbeat_subscriber; _ze_advanced_subscriber_state_rc_t _state; } _ze_advanced_subscriber_t; _Z_OWNED_TYPE_VALUE_PREFIX(ze, _ze_advanced_subscriber_t, advanced_subscriber) _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(ze, advanced_subscriber) typedef struct { size_t _id; _ze_advanced_subscriber_state_weak_t _statesref; } _ze_sample_miss_listener_t; static inline _ze_sample_miss_listener_t _ze_sample_miss_listener_null(void) { return (_ze_sample_miss_listener_t){0}; } static inline bool _ze_sample_miss_listener_check(const _ze_sample_miss_listener_t *miss_listener) { return !_Z_SIMPLE_RC_IS_NULL(&miss_listener->_statesref); } _Z_OWNED_TYPE_VALUE_PREFIX(ze, _ze_sample_miss_listener_t, sample_miss_listener) _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(ze, sample_miss_listener) #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 /** * Settings for retrieving historical data for Advanced Subscriber. * * Members: * bool is_enabled: Must be set to ``true``, to enable the history data recovery. * bool detect_late_publishers: Enable detection of late joiner publishers and query for * their historical data. Late joiner detection can only be achieved for Publishers that * enable publisher_detection. History can only be retransmitted by Publishers that enable * caching. * size_t max_samples: Number of samples to query for each resource. ``0`` corresponds to no * limit on number of samples. * uint64_t max_age_ms: Maximum age of samples to query. ``0`` corresponds to no limit on samples' * age. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { bool is_enabled; bool detect_late_publishers; size_t max_samples; uint64_t max_age_ms; } ze_advanced_subscriber_history_options_t; /** * Settings for detection of the last sample(s) miss by Advanced Subscriber. * * Members: * bool is_enabled: Must be set to ``true``, to enable the last sample(s) miss detection. * uint64_t periodic_queries_period_ms: Period for queries for not yet received Samples. * These queries allow to retrieve the last Sample(s) if the last Sample(s) is/are lost. * So it is useful for sporadic publications but useless for periodic publications with a * period smaller or equal to this period. If set to 0, the last sample(s) miss detection * will be performed based on publisher's heartbeat if the latter is enabled. * * Note: periodic queries require the periodic scheduler to be started via :c:func:`zp_start_periodic_scheduler_task`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { bool is_enabled; uint64_t periodic_queries_period_ms; } ze_advanced_subscriber_last_sample_miss_detection_options_t; /** * Settings for recovering lost messages for Advanced Subscriber. * * Members: * bool is_enabled: Must be set to ``true``, to enable the lost sample recovery. * ze_advanced_subscriber_last_sample_miss_detection_options_t last_sample_miss_detection: * Setting for detecting last sample(s) miss. Note that it does not affect intermediate sample * miss detection/retrieval (which is performed automatically as long as recovery is enabled). * If this option is disabled, subscriber will be unable to detect/request retransmission of * missed sample until it receives a more recent one from the same publisher. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { bool is_enabled; ze_advanced_subscriber_last_sample_miss_detection_options_t last_sample_miss_detection; } ze_advanced_subscriber_recovery_options_t; /** * Represents the set of options that can be applied to an advanced subscriber, * upon its declaration via :c:func:`ze_declare_advanced_subscriber`. * * Members: * z_subscriber_options_t subscriber_options: Base subscriber options. * ze_advanced_subscriber_history_options_t history: Settings for querying historical data. * History can only be retransmitted by Publishers that enable caching. * ze_advanced_subscriber_recovery_options_t recovery: Settings for retransmission of detected * lost Samples. Retransmission of lost samples can only be done by Publishers that enable * caching and sample_miss_detection. * uint64_t query_timeout_ms: Timeout to be used for history and recovery queries. Default value * will be used if set to ``0``. * bool subscriber_detection: Allow this subscriber to be detected through liveliness. * const z_loaned_keyexpr_t *subscriber_detection_metadata: An optional key expression to be added * to the liveliness token key expression. It can be used to convey meta data. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { z_subscriber_options_t subscriber_options; ze_advanced_subscriber_history_options_t history; ze_advanced_subscriber_recovery_options_t recovery; uint64_t query_timeout_ms; bool subscriber_detection; const z_loaned_keyexpr_t *subscriber_detection_metadata; } ze_advanced_subscriber_options_t; /** * Declares an advanced subscriber for a given keyexpr. Note that dropping the subscriber drops its * callback. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the advanced subscriber through. * subscriber: Pointer to an uninitialized :c:type:`ze_owned_advanced_subscriber_t`. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to subscribe to. * callback: Pointer to a :c:type:`z_moved_closure_sample_t` that will be called each time a data * matching the subscribed expression is received. * options: Pointer to a :c:type:`ze_advanced_subscriber_options_t` to configure the operation. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_declare_advanced_subscriber(const z_loaned_session_t *zs, ze_owned_advanced_subscriber_t *subscriber, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, ze_advanced_subscriber_options_t *options); /** * Declares a background advanced subscriber. Subscriber callback will be called to process the messages, * until the corresponding session is closed or dropped. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the advanced subscriber through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to subscribe to. * callback: Pointer to a :c:type:`z_moved_closure_sample_t` that will be called each time a data * matching the subscribed expression is received. * options: Pointer to a :c:type:`ze_advanced_subscriber_options_t` to configure the operation. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_declare_background_advanced_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, ze_advanced_subscriber_options_t *options); /** * Undeclares the advanced subscriber. * * Parameters: * subscriber: Moved :c:type:`ze_owned_advanced_subscriber_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_undeclare_advanced_subscriber(ze_moved_advanced_subscriber_t *subscriber); /** * Gets the keyexpr from an advanced subscriber. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ const z_loaned_keyexpr_t *ze_advanced_subscriber_keyexpr(const ze_loaned_advanced_subscriber_t *subscriber); /** * Gets the entity global Id from an advanced subscriber. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` to get the entity global Id from. * * Return: * The entity gloabl Id wrapped as a :c:type:`z_entity_global_global_id_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_entity_global_id_t ze_advanced_subscriber_id(const ze_loaned_advanced_subscriber_t *subscriber); /** * Declares a sample miss listener, registering a callback for notifying subscriber about missed samples. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` instance to associate with sample miss listener. * sample_miss_listener: Pointer to an uninitialized :c:type:`ze_owned_sample_miss_listener_t` where sample miss * listener will be constructed. The sample miss listener's callback will be automatically dropped when the * subscriber is dropped. callback: Pointer to a :c:type:`ze_moved_closure_miss_t` that will be called every time a * sample miss is detected. * * Return: * ``0`` if successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_subscriber_declare_sample_miss_listener(const ze_loaned_advanced_subscriber_t *subscriber, ze_owned_sample_miss_listener_t *sample_miss_listener, ze_moved_closure_miss_t *callback); /** * Declares a sample miss listener, registering a callback for notifying subscriber about missed samples. * The callback will be run in the background until the corresponding subscriber is dropped. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` instance to associate with sample miss listener. * callback: Pointer to a :c:type:`ze_moved_closure_miss_t` that will be called every time a sample miss is detected. * * Return: * ``0`` if successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_subscriber_declare_background_sample_miss_listener( const ze_loaned_advanced_subscriber_t *subscriber, ze_moved_closure_miss_t *callback); /** * Undeclares the given sample miss listener, droping and invalidating it. * * Parameters: * sample_miss_listener: Moved :c:type:`ze_moved_sample_miss_listener_t` to undeclare * * Return: * ``0`` if successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_undeclare_sample_miss_listener(ze_moved_sample_miss_listener_t *sample_miss_listener); /** * Declares a liveliness token listener for matching publishers detection. Only advanced publishers, enabling publisher * detection can be detected. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` instance. * liveliness_subscriber: Pointer to an uninitialized :c:type:`z_owned_subscriber_t` where the liveliness subscriber * will be constructed. * callback: Pointer to a :c:type:`z_moved_closure_sample_t` that will be called each time a liveliness token status * is changed. * options: Pointer to a :c:type:`z_liveliness_subscriber_options_t` to configure the liveliness subscriber. * * Return: * ``0`` if successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_subscriber_detect_publishers(const ze_loaned_advanced_subscriber_t *subscriber, z_owned_subscriber_t *liveliness_subscriber, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options); /** * Declares a background subscriber on liveliness tokens of matching publishers. Subscriber callback will be called to * process the messages, until the corresponding session is closed or dropped. Only advanced publishers, enabling * publisher detection can be detected. * * Parameters: * subscriber: Pointer to a :c:type:`ze_loaned_advanced_subscriber_t` instance. * callback: Pointer to a :c:type:`z_moved_closure_sample_t` that will be called each time a liveliness token status * is changed. * options: Pointer to a :c:type:`z_liveliness_subscriber_options_t` to configure the liveliness subscriber. * * Return: * ``0`` if successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_advanced_subscriber_detect_publishers_background(const ze_loaned_advanced_subscriber_t *subscriber, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options); /** * Builds a :c:type:`ze_advanced_subscriber_history_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_subscriber_history_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_subscriber_history_options_default(ze_advanced_subscriber_history_options_t *options); /** * Builds a :c:type:`ze_advanced_subscriber_recovery_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_subscriber_recovery_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_subscriber_recovery_options_default(ze_advanced_subscriber_recovery_options_t *options); /** * Builds a :c:type:`ze_advanced_subscriber_last_sample_miss_detection_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_subscriber_last_sample_miss_detection_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_subscriber_last_sample_miss_detection_options_default( ze_advanced_subscriber_last_sample_miss_detection_options_t *options); /** * Builds a :c:type:`ze_advanced_subscriber_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`ze_advanced_subscriber_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_advanced_subscriber_options_default(ze_advanced_subscriber_options_t *options); #endif // Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 #endif // Z_FEATURE_UNSTABLE_API #ifdef __cplusplus } #endif #endif // INCLUDE_ZENOH_PICO_API_ADVANCED_SUBSCRIBER_H ================================================ FILE: include/zenoh-pico/api/constants.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_API_CONSTANTS_H #define ZENOH_PICO_API_CONSTANTS_H #define Z_SELECTOR_TIME "_time=" #define Z_SELECTOR_QUERY_MATCH "_anyke" #ifdef __cplusplus extern "C" { #endif /** * What bitmask for scouting. * * Enumerators: * Z_WHAT_ROUTER: Router. * Z_WHAT_PEER: Peer. * Z_WHAT_CLIENT: Client. */ typedef enum { Z_WHAT_ROUTER = 0x01, // Router Z_WHAT_PEER = 0x02, // Peer Z_WHAT_CLIENT = 0x04, // Client Z_WHAT_ROUTER_PEER = (0x01 | 0x02), Z_WHAT_ROUTER_CLIENT = (0x01 | 0x04), Z_WHAT_PEER_CLIENT = (0x02 | 0x04), Z_WHAT_ROUTER_PEER_CLIENT = ((0x01 | 0x02) | 0x04), } z_what_t; /** * Whatami values, defined as a bitmask. * * Enumerators: * Z_WHATAMI_ROUTER: Bitmask to filter Zenoh routers. * Z_WHATAMI_PEER: Bitmask to filter for Zenoh peers. * Z_WHATAMI_CLIENT: Bitmask to filter for Zenoh clients. */ typedef enum z_whatami_t { Z_WHATAMI_ROUTER = 0x01, Z_WHATAMI_PEER = 0x02, Z_WHATAMI_CLIENT = 0x04, } z_whatami_t; #define Z_WHATAMI_DEFAULT Z_WHATAMI_ROUTER; /** * The locality of samples to be received by subscribers or targeted by publishers. * * Enumerators: * Z_LOCALITY_ANY: Allow both session-local and remote traffic. * Z_LOCALITY_SESSION_LOCAL: Allow session-local traffic only. * Z_LOCALITY_REMOTE: Allow remote traffic only. */ typedef enum z_locality_t { Z_LOCALITY_ANY = 0, Z_LOCALITY_SESSION_LOCAL = 1, Z_LOCALITY_REMOTE = 2, } z_locality_t; static inline z_locality_t z_locality_default(void) { return Z_LOCALITY_ANY; } /** * Status values for keyexpr canonization operation. * Used as return value of canonization-related functions, * like :c:func:`z_keyexpr_is_canon` or :c:func:`z_keyexpr_canonize`. * * Enumerators: * Z_KEYEXPR_CANON_SUCCESS: The key expression is canon. * Z_KEYEXPR_CANON_LONE_DOLLAR_STAR: The key contains a ``$*`` chunk, which must be replaced by ``*``. * Z_KEYEXPR_CANON_SINGLE_STAR_AFTER_DOUBLE_STAR: The key contains ``** / *``, which must be replaced by ``* / **``. * Z_KEYEXPR_CANON_DOUBLE_STAR_AFTER_DOUBLE_STAR: The key contains ``** / **``, which must be replaced by ``**``. * Z_KEYEXPR_CANON_EMPTY_CHUNK: The key contains empty chunks. * Z_KEYEXPR_CANON_STARS_IN_CHUNK: The key contains a ``*`` in a chunk without being escaped by a DSL, which is * forbidden. * Z_KEYEXPR_CANON_DOLLAR_AFTER_DOLLAR_OR_STAR: The key contains ``$*$`` or ``$$``, which is forbidden. * Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK: The key contains ``#`` or ``?``, which is forbidden. * Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR: The key contains a ``$`` which is not bound to a DSL. */ typedef enum { Z_KEYEXPR_CANON_SUCCESS = 0, Z_KEYEXPR_CANON_LONE_DOLLAR_STAR = -1, Z_KEYEXPR_CANON_SINGLE_STAR_AFTER_DOUBLE_STAR = -2, Z_KEYEXPR_CANON_DOUBLE_STAR_AFTER_DOUBLE_STAR = -3, Z_KEYEXPR_CANON_EMPTY_CHUNK = -4, Z_KEYEXPR_CANON_STARS_IN_CHUNK = -5, Z_KEYEXPR_CANON_DOLLAR_AFTER_DOLLAR_OR_STAR = -6, Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK = -7, Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR = -8 } zp_keyexpr_canon_status_t; /** * Intersection level of two key expressions. * * Enumerators: * Z_KEYEXPR_INTERSECTION_LEVEL_DISJOINT: The two key expressions do not intersect. * Z_KEYEXPR_INTERSECTION_LEVEL_INTERSECTS: The two key expressions intersect, i.e. there exists at least one key * expression that is included by both. * Z_KEYEXPR_INTERSECTION_LEVEL_INCLUDES: The first key expression is the superset of the second one. * Z_KEYEXPR_INTERSECTION_LEVEL_EQUALS: The two key expressions are equal. */ typedef enum { Z_KEYEXPR_INTERSECTION_LEVEL_DISJOINT = 0, Z_KEYEXPR_INTERSECTION_LEVEL_INTERSECTS = 1, Z_KEYEXPR_INTERSECTION_LEVEL_INCLUDES = 2, Z_KEYEXPR_INTERSECTION_LEVEL_EQUALS = 3, } z_keyexpr_intersection_level_t; /** * Key expression constant strings. */ #define _Z_KEYEXPR_AT "@" #define _Z_KEYEXPR_AT_LEN (sizeof(_Z_KEYEXPR_AT) - 1) #define _Z_KEYEXPR_ADV_PREFIX "@adv" #define _Z_KEYEXPR_ADV_PREFIX_LEN (sizeof(_Z_KEYEXPR_ADV_PREFIX) - 1) #define _Z_KEYEXPR_PUB "pub" #define _Z_KEYEXPR_PUB_LEN (sizeof(_Z_KEYEXPR_PUB) - 1) #define _Z_KEYEXPR_SUB "sub" #define _Z_KEYEXPR_SUB_LEN (sizeof(_Z_KEYEXPR_SUB) - 1) #define _Z_KEYEXPR_UHLC "uhlc" #define _Z_KEYEXPR_UHLC_LEN (sizeof(_Z_KEYEXPR_UHLC) - 1) #define _Z_KEYEXPR_EMPTY "_" #define _Z_KEYEXPR_EMPTY_LEN (sizeof(_Z_KEYEXPR_EMPTY) - 1) #define _Z_KEYEXPR_STAR "*" #define _Z_KEYEXPR_STAR_LEN (sizeof(_Z_KEYEXPR_STAR) - 1) #define _Z_KEYEXPR_STARSTAR "**" #define _Z_KEYEXPR_STARSTAR_LEN (sizeof(_Z_KEYEXPR_STARSTAR) - 1) #define _Z_KEYEXPR_SESSION "session" #define _Z_KEYEXPR_SESSION_LEN (sizeof(_Z_KEYEXPR_SESSION) - 1) #define _Z_KEYEXPR_PICO "pico" #define _Z_KEYEXPR_PICO_LEN (sizeof(_Z_KEYEXPR_PICO) - 1) #define _Z_KEYEXPR_TRANSPORTS "transports" #define _Z_KEYEXPR_TRANSPORTS_LEN (sizeof(_Z_KEYEXPR_TRANSPORTS) - 1) #define _Z_KEYEXPR_TRANSPORT_UNICAST "transport/unicast" #define _Z_KEYEXPR_TRANSPORT_UNICAST_LEN (sizeof(_Z_KEYEXPR_TRANSPORT_UNICAST) - 1) #define _Z_KEYEXPR_TRANSPORT_MULTICAST "transport/multicast" #define _Z_KEYEXPR_TRANSPORT_MULTICAST_LEN (sizeof(_Z_KEYEXPR_TRANSPORT_MULTICAST) - 1) #define _Z_KEYEXPR_TRANSPORT_RAWETH "transport/raweth" #define _Z_KEYEXPR_TRANSPORT_RAWETH_LEN (sizeof(_Z_KEYEXPR_TRANSPORT_RAWETH) - 1) #define _Z_KEYEXPR_LINK "link" #define _Z_KEYEXPR_LINK_LEN (sizeof(_Z_KEYEXPR_LINK) - 1) #define _Z_KEYEXPR_PEERS "peers" #define _Z_KEYEXPR_PEERS_LEN (sizeof(_Z_KEYEXPR_PEERS) - 1) #define _Z_KEYEXPR_SEPARATOR "/" #define _Z_KEYEXPR_SEPARATOR_LEN (sizeof(_Z_KEYEXPR_SEPARATOR) - 1) /** * Sample kind values. * * Enumerators: * Z_SAMPLE_KIND_PUT: The Sample was issued by a ``put`` operation. * Z_SAMPLE_KIND_DELETE: The Sample was issued by a ``delete`` operation. */ typedef enum { Z_SAMPLE_KIND_PUT = 0, Z_SAMPLE_KIND_DELETE = 1, Z_SAMPLE_KIND_DEFAULT = Z_SAMPLE_KIND_PUT } z_sample_kind_t; /** * Consolidation mode values. * * Enumerators: * Z_CONSOLIDATION_MODE_AUTO: Let Zenoh decide the best consolidation mode depending on the query selector. * Z_CONSOLIDATION_MODE_NONE: No consolidation is applied. Replies may come in any order and any number. * Z_CONSOLIDATION_MODE_MONOTONIC: It guarantees that any reply for a given key expression will be monotonic in time * w.r.t. the previous received replies for the same key expression. I.e., for the same key expression multiple * replies may be received. It is guaranteed that two replies received at t1 and t2 will have timestamp * ts2 > ts1. It optimizes latency. * Z_CONSOLIDATION_MODE_LATEST: It guarantees unicity of replies for the same key expression. * It optimizes bandwidth. */ typedef enum { Z_CONSOLIDATION_MODE_AUTO = -1, Z_CONSOLIDATION_MODE_NONE = 0, Z_CONSOLIDATION_MODE_MONOTONIC = 1, Z_CONSOLIDATION_MODE_LATEST = 2, Z_CONSOLIDATION_MODE_DEFAULT = Z_CONSOLIDATION_MODE_AUTO } z_consolidation_mode_t; /** * Reliability values. * * Enumerators: * Z_RELIABILITY_BEST_EFFORT: Defines reliability as ``BEST_EFFORT`` * Z_RELIABILITY_RELIABLE: Defines reliability as ``RELIABLE`` * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef enum { Z_RELIABILITY_BEST_EFFORT = 1, Z_RELIABILITY_RELIABLE = 0, Z_RELIABILITY_DEFAULT = Z_RELIABILITY_RELIABLE } z_reliability_t; /** * Congestion control values. * * Enumerators: * Z_CONGESTION_CONTROL_BLOCK: Defines congestion control as ``BLOCK``. Messages are not dropped in case of * congestion control. * Z_CONGESTION_CONTROL_DROP: Defines congestion control as ``DROP``. Messages are dropped in case * of congestion control. */ typedef enum { Z_CONGESTION_CONTROL_BLOCK = 1, Z_CONGESTION_CONTROL_DROP = 0, Z_CONGESTION_CONTROL_DEFAULT = Z_CONGESTION_CONTROL_DROP } z_congestion_control_t; static inline z_congestion_control_t z_internal_congestion_control_default_push(void) { return Z_CONGESTION_CONTROL_DROP; } static inline z_congestion_control_t z_internal_congestion_control_default_request(void) { return Z_CONGESTION_CONTROL_BLOCK; } /** * Priority of Zenoh messages values. * * Enumerators: * _Z_PRIORITY_CONTROL: Priority for ``Control`` messages. * Z_PRIORITY_REAL_TIME: Priority for ``RealTime`` messages. * Z_PRIORITY_INTERACTIVE_HIGH: Highest priority for ``Interactive`` messages. * Z_PRIORITY_INTERACTIVE_LOW: Lowest priority for ``Interactive`` messages. * Z_PRIORITY_DATA_HIGH: Highest priority for ``Data`` messages. * Z_PRIORITY_DATA: Default priority for ``Data`` messages. * Z_PRIORITY_DATA_LOW: Lowest priority for ``Data`` messages. * Z_PRIORITY_BACKGROUND: Priority for ``Background traffic`` messages. */ typedef enum { _Z_PRIORITY_CONTROL = 0, Z_PRIORITY_REAL_TIME = 1, Z_PRIORITY_INTERACTIVE_HIGH = 2, Z_PRIORITY_INTERACTIVE_LOW = 3, Z_PRIORITY_DATA_HIGH = 4, Z_PRIORITY_DATA = 5, Z_PRIORITY_DATA_LOW = 6, Z_PRIORITY_BACKGROUND = 7, Z_PRIORITY_DEFAULT = Z_PRIORITY_DATA } z_priority_t; /** * Query target values. * * Enumerators: * Z_QUERY_TARGET_BEST_MATCHING: The nearest complete queryable if any else all matching queryables. * Z_QUERY_TARGET_ALL: All matching queryables. * Z_QUERY_TARGET_ALL_COMPLETE: A set of complete queryables. */ typedef enum { Z_QUERY_TARGET_BEST_MATCHING = 0, Z_QUERY_TARGET_ALL = 1, Z_QUERY_TARGET_ALL_COMPLETE = 2, Z_QUERY_TARGET_DEFAULT = Z_QUERY_TARGET_BEST_MATCHING } z_query_target_t; /** * The kinds of accepted query replies. * * The queryable may serve glob-like key expressions. * E.g., the queryable may be declared with the key expression `foo/b$*`. * At the same time, it may send replies with more specific key expressions, e.g., `foo/bar` or `foo/baz`. * This may cause a situation when the queryable receives a query with the key expression `foo/bar` * and replies to it with the key expression `foo/baz`. * By default, this behavior is not allowed. Calling `z_query_reply` value on a query for `foo/bar` with key expression * `foo/baz` will result in an error on the sending side. But if the query is sent with the `accept_replies` flag set to * `Z_REPLY_KEYEXPR_ANY` in either `z_get_options_t` or `z_querier_options_t`, then the reply with a disjoint key * expression will be accepted for this query. * * The queryable may check whether disjoint replies are allowed for a query with `z_query_accepts_replies` function. * * Enumerators: * Z_REPLY_KEYEXPR_ANY: Accept replies on any key expression. * Z_REPLY_KEYEXPR_MATCHING_QUERY: Accept replies only to intersecting key expressions intersecting with query's own * key expression. */ typedef enum z_reply_keyexpr_t { Z_REPLY_KEYEXPR_ANY = 0, Z_REPLY_KEYEXPR_MATCHING_QUERY = 1, Z_REPLY_KEYEXPR_DEFAULT = Z_REPLY_KEYEXPR_MATCHING_QUERY } z_reply_keyexpr_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_API_CONSTANTS_H */ ================================================ FILE: include/zenoh-pico/api/encoding.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_API_ENCODING_H #define ZENOH_PICO_API_ENCODING_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif /** * Default encoding values used by Zenoh. * * An encoding has a similar role to Content-type in HTTP: it indicates, when present, how data should be interpreted by * the application. * * Please note the Zenoh protocol does not impose any encoding value nor it operates on it. * It can be seen as some optional metadata that is carried over by Zenoh in such a way the application may perform * different operations depending on the encoding value. * * A set of associated constants are provided to cover the most common encodings for user convenience. * This is particularly useful in helping Zenoh to perform additional wire-level optimizations. * * Register your encoding metadata from a string with :c:func:`z_encoding_from_str`. To get the optimization, you need * Z_FEATURE_ENCODING_VALUES to 1 and your string should follow the format: ";" * * E.g: "text/plain;utf8" * * Or you can set the value to the constants directly with this list of constants: */ #if Z_FEATURE_ENCODING_VALUES == 1 // - Below are Primitives types, supported in all Zenoh bindings /** * Just some bytes. * * Constant alias for string: `"zenoh/bytes"`. * * This encoding supposes that the payload was created with c:func:`z_bytes_from_buf`, c:func:`z_bytes_from_slice` or * similar functions and its data can be accessed via c:func:`z_bytes_to_slice`. */ const z_loaned_encoding_t *z_encoding_zenoh_bytes(void); extern const z_owned_encoding_t ZP_ENCODING_ZENOH_BYTES; /** * A UTF-8 string. * Constant alias for string: `"zenoh/string"`. * * This encoding supposes that the payload was created with c:func:`z_bytes_from_str`, c:func:`z_bytes_from_string` or * similar functions and its data can be accessed via c:func:`z_bytes_to_string`. */ const z_loaned_encoding_t *z_encoding_zenoh_string(void); extern const z_owned_encoding_t ZP_ENCODING_ZENOH_STRING; /** * Zenoh serialized data. * Constant alias for string: `"zenoh/serialized"`. * * This encoding supposes that the payload was created with serialization functions. * The `schema` field may contain the details of serialziation format. */ const z_loaned_encoding_t *z_encoding_zenoh_serialized(void); extern const z_owned_encoding_t ZP_ENCODING_ZENOH_SERIALIZED; /** * An application-specific stream of bytes. * Constant alias for string: `"application/octet-stream"`. */ const z_loaned_encoding_t *z_encoding_application_octet_stream(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_OCTET_STREAM; /** * A textual file. * Constant alias for string: `"text/plain"`. */ const z_loaned_encoding_t *z_encoding_text_plain(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_PLAIN; /** * JSON data intended to be consumed by an application. * Constant alias for string: `"application/json"`. */ const z_loaned_encoding_t *z_encoding_application_json(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JSON; /** * JSON data intended to be human readable. * Constant alias for string: `"text/json"`. */ const z_loaned_encoding_t *z_encoding_text_json(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_JSON; /** * A Common Data Representation (CDR)-encoded data. * Constant alias for string: `"application/cdr"`. */ const z_loaned_encoding_t *z_encoding_application_cdr(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_CDR; /** * A Concise Binary Object Representation (CBOR)-encoded data. * Constant alias for string: `"application/cbor"`. */ const z_loaned_encoding_t *z_encoding_application_cbor(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_CBOR; /** * YAML data intended to be consumed by an application. * Constant alias for string: `"application/yaml"`. */ const z_loaned_encoding_t *z_encoding_application_yaml(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_YAML; /** * YAML data intended to be human readable. * Constant alias for string: `"text/yaml"`. */ const z_loaned_encoding_t *z_encoding_text_yaml(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_YAML; /** * JSON5 encoded data that are human readable. * Constant alias for string: `"text/json5"`. */ const z_loaned_encoding_t *z_encoding_text_json5(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_JSON5; /** * A Python object serialized using `pickle `_. * Constant alias for string: `"application/python-serialized-object"`. */ const z_loaned_encoding_t *z_encoding_application_python_serialized_object(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_PYTHON_SERIALIZED_OBJECT; /** * An application-specific protobuf-encoded data. * Constant alias for string: `"application/protobuf"`. */ const z_loaned_encoding_t *z_encoding_application_protobuf(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_PROTOBUF; /** * A Java serialized object. * Constant alias for string: `"application/java-serialized-object"`. */ const z_loaned_encoding_t *z_encoding_application_java_serialized_object(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JAVA_SERIALIZED_OBJECT; /** * An `openmetrics `_ data, commonly used by * `Prometheus `_. * Constant alias for string: `"application/openmetrics-text"`. */ const z_loaned_encoding_t *z_encoding_application_openmetrics_text(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_OPENMETRICS_TEXT; /** * A Portable Network Graphics (PNG) image. * Constant alias for string: `"image/png"`. */ const z_loaned_encoding_t *z_encoding_image_png(void); extern const z_owned_encoding_t ZP_ENCODING_IMAGE_PNG; /** * A Joint Photographic Experts Group (JPEG) image. * Constant alias for string: `"image/jpeg"`. */ const z_loaned_encoding_t *z_encoding_image_jpeg(void); extern const z_owned_encoding_t ZP_ENCODING_IMAGE_JPEG; /** * A Graphics Interchange Format (GIF) image. * Constant alias for string: `"image/gif"`. */ const z_loaned_encoding_t *z_encoding_image_gif(void); extern const z_owned_encoding_t ZP_ENCODING_IMAGE_GIF; /** * A BitMap (BMP) image. * Constant alias for string: `"image/bmp"`. */ const z_loaned_encoding_t *z_encoding_image_bmp(void); extern const z_owned_encoding_t ZP_ENCODING_IMAGE_BMP; /** * A Web Portable (WebP) image. * Constant alias for string: `"image/webp"`. */ const z_loaned_encoding_t *z_encoding_image_webp(void); extern const z_owned_encoding_t ZP_ENCODING_IMAGE_WEBP; /** * An XML file intended to be consumed by an application. * Constant alias for string: `"application/xml"`. */ const z_loaned_encoding_t *z_encoding_application_xml(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_XML; /** * An encoded list of tuples, each consisting of a name and a value. * Constant alias for string: `"application/x-www-form-urlencoded"`. */ const z_loaned_encoding_t *z_encoding_application_x_www_form_urlencoded(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_X_WWW_FORM_URLENCODED; /** * An HTML file. * Constant alias for string: `"text/html"`. */ const z_loaned_encoding_t *z_encoding_text_html(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_HTML; /** * An XML file that is human-readable. * Constant alias for string: `"text/xml"`. */ const z_loaned_encoding_t *z_encoding_text_xml(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_XML; /** * A CSS file. * Constant alias for string: `"text/css"`. */ const z_loaned_encoding_t *z_encoding_text_css(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_CSS; /** * A JavaScript file. * Constant alias for string: `"text/javascript"`. */ const z_loaned_encoding_t *z_encoding_text_javascript(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_JAVASCRIPT; /** * A Markdown file. * Constant alias for string: `"text/markdown"`. */ const z_loaned_encoding_t *z_encoding_text_markdown(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_MARKDOWN; /** * A CSV file. * Constant alias for string: `"text/csv"`. */ const z_loaned_encoding_t *z_encoding_text_csv(void); extern const z_owned_encoding_t ZP_ENCODING_TEXT_CSV; /** * An application-specific SQL query. * Constant alias for string: `"application/sql"`. */ const z_loaned_encoding_t *z_encoding_application_sql(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_SQL; /** * Constrained Application Protocol (CoAP) data intended for CoAP-to-HTTP and HTTP-to-CoAP proxies. * Constant alias for string: `"application/coap-payload"`. */ const z_loaned_encoding_t *z_encoding_application_coap_payload(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_COAP_PAYLOAD; /** * Defines a JSON document structure for expressing a sequence of operations to apply to a JSON document. * Constant alias for string: `"application/json-patch+json"`. */ const z_loaned_encoding_t *z_encoding_application_json_patch_json(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JSON_PATCH_JSON; /** * A JSON text sequence consists of any number of JSON texts, all encoded in UTF-8. * Constant alias for string: `"application/json-seq"`. */ const z_loaned_encoding_t *z_encoding_application_json_seq(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JSON_SEQ; /** * A JSONPath defines a string syntax for selecting and extracting JSON values from within a given JSON value. * Constant alias for string: `"application/jsonpath"`. */ const z_loaned_encoding_t *z_encoding_application_jsonpath(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JSONPATH; /** * A JSON Web Token (JWT). * Constant alias for string: `"application/jwt"`. */ const z_loaned_encoding_t *z_encoding_application_jwt(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_JWT; /** * An application-specific MPEG-4 encoded data, either audio or video. * Constant alias for string: `"application/mp4"`. */ const z_loaned_encoding_t *z_encoding_application_mp4(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_MP4; /** * A SOAP 1.2 message serialized as XML 1.0. * Constant alias for string: `"application/soap+xml"`. */ const z_loaned_encoding_t *z_encoding_application_soap_xml(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_SOAP_XML; /** * A YANG-encoded data commonly used by the Network Configuration Protocol (NETCONF). * Constant alias for string: `"application/yang"`. */ const z_loaned_encoding_t *z_encoding_application_yang(void); extern const z_owned_encoding_t ZP_ENCODING_APPLICATION_YANG; /** * A MPEG-4 Advanced Audio Coding (AAC) media. * Constant alias for string: `"audio/aac"`. */ const z_loaned_encoding_t *z_encoding_audio_aac(void); extern const z_owned_encoding_t ZP_ENCODING_AUDIO_AAC; /** * A Free Lossless Audio Codec (FLAC) media. * Constant alias for string: `"audio/flac"`. */ const z_loaned_encoding_t *z_encoding_audio_flac(void); extern const z_owned_encoding_t ZP_ENCODING_AUDIO_FLAC; /** * An audio codec defined in MPEG-1, MPEG-2, MPEG-4, or registered at the MP4 registration authority. * Constant alias for string: `"audio/mp4"`. */ const z_loaned_encoding_t *z_encoding_audio_mp4(void); extern const z_owned_encoding_t ZP_ENCODING_AUDIO_MP4; /** * An Ogg-encapsulated audio stream. * Constant alias for string: `"audio/ogg"`. */ const z_loaned_encoding_t *z_encoding_audio_ogg(void); extern const z_owned_encoding_t ZP_ENCODING_AUDIO_OGG; /** * A Vorbis-encoded audio stream. * Constant alias for string: `"audio/vorbis"`. */ const z_loaned_encoding_t *z_encoding_audio_vorbis(void); extern const z_owned_encoding_t ZP_ENCODING_AUDIO_VORBIS; /** * A h261-encoded video stream. * Constant alias for string: `"video/h261"`. */ const z_loaned_encoding_t *z_encoding_video_h261(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_H261; /** * A h263-encoded video stream. * Constant alias for string: `"video/h263"`. */ const z_loaned_encoding_t *z_encoding_video_h263(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_H263; /** * A h264-encoded video stream. * Constant alias for string: `"video/h264"`. */ const z_loaned_encoding_t *z_encoding_video_h264(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_H264; /** * A h265-encoded video stream. * Constant alias for string: `"video/h265"`. */ const z_loaned_encoding_t *z_encoding_video_h265(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_H265; /** * A h266-encoded video stream. * Constant alias for string: `"video/h266"`. */ const z_loaned_encoding_t *z_encoding_video_h266(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_H266; /** * A video codec defined in MPEG-1, MPEG-2, MPEG-4, or registered at the MP4 registration authority. * Constant alias for string: `"video/mp4"`. */ const z_loaned_encoding_t *z_encoding_video_mp4(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_MP4; /** * An Ogg-encapsulated video stream. * Constant alias for string: `"video/ogg"`. */ const z_loaned_encoding_t *z_encoding_video_ogg(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_OGG; /** * An uncompressed, studio-quality video stream. * Constant alias for string: `"video/raw"`. */ const z_loaned_encoding_t *z_encoding_video_raw(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_RAW; /** * A VP8-encoded video stream. * Constant alias for string: `"video/vp8"`. */ const z_loaned_encoding_t *z_encoding_video_vp8(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_VP8; /** * A VP9-encoded video stream. * Constant alias for string: `"video/vp9"`. */ const z_loaned_encoding_t *z_encoding_video_vp9(void); extern const z_owned_encoding_t ZP_ENCODING_VIDEO_VP9; /** * Returns a loaned default `z_loaned_encoding_t`. */ const z_loaned_encoding_t *z_encoding_loan_default(void); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_API_ENCODING_H */ ================================================ FILE: include/zenoh-pico/api/handlers.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_HANDLERS_H #define INCLUDE_ZENOH_PICO_API_HANDLERS_H #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/fifo_mt.h" #include "zenoh-pico/collections/ring_mt.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif // -- Channel #define _Z_CHANNEL_DEFINE_IMPL(handler_type, handler_name, handler_new_f_name, callback_type, callback_new_f, \ collection_type, collection_new_f, collection_clear_f, collection_push_f, \ collection_pull_f, collection_try_pull_f, collection_close_f, elem_owned_type, \ elem_loaned_type, elem_take_f, elem_move_f, elem_drop_f, elem_null_f) \ typedef struct { \ collection_type collection; \ } handler_type; \ \ static inline void _z_##handler_name##_elem_free(void **elem) { \ elem_drop_f(elem_move_f((elem_owned_type *)*elem)); \ z_free(*elem); \ *elem = NULL; \ } \ static inline void _z_##handler_name##_elem_move(void *dst, void *src) { \ memcpy(dst, src, sizeof(elem_owned_type)); \ z_free(src); \ } \ \ static inline void _z_##handler_name##_clear(handler_type *handler) { \ if (handler != NULL) { \ collection_clear_f(&handler->collection, _z_##handler_name##_elem_free); \ } \ } \ _Z_REFCOUNT_DEFINE(_z_##handler_name, _z_##handler_name) \ _Z_OWNED_TYPE_RC(_z_##handler_name##_rc_t, handler_name) \ _Z_OWNED_FUNCTIONS_RC_INLINE_IMPL(handler_name) \ static inline void _z_##handler_name##_close(void *context) { \ _z_##handler_name##_rc_t *handler = (_z_##handler_name##_rc_t *)context; \ if (_z_rc_strong_count(handler->_cnt) > 1) { \ z_result_t ret = collection_close_f(&_Z_RC_IN_VAL(handler)->collection); \ if (ret < 0) { \ _Z_ERROR("%s failed: %i", #collection_push_f, ret); \ } \ } \ _z_##handler_name##_rc_drop(handler); \ z_free(handler); \ } \ static inline void _z_##handler_name##_send(elem_loaned_type *elem, void *context) { \ _z_##handler_name##_rc_t *handler = (_z_##handler_name##_rc_t *)context; \ if (_z_rc_strong_count(handler->_cnt) > 1) { \ elem_owned_type *internal_elem = (elem_owned_type *)z_malloc(sizeof(elem_owned_type)); \ if (internal_elem == NULL) { \ _Z_ERROR("Out of memory"); \ return; \ } \ elem_take_f(internal_elem, elem); \ z_result_t ret = \ collection_push_f(internal_elem, &_Z_RC_IN_VAL(handler)->collection, _z_##handler_name##_elem_free); \ if (ret != _Z_RES_OK) { \ _Z_ERROR("%s failed: %i", #collection_push_f, ret); \ } \ } \ } \ \ static inline z_result_t handler_new_f_name(callback_type *callback, z_owned_##handler_name##_t *handler, \ size_t capacity) { \ if (capacity < 1) { \ _Z_ERROR_RETURN(_Z_ERR_INVALID); \ } \ _z_##handler_name##_t h; \ _Z_RETURN_IF_ERR(collection_new_f(&h.collection, capacity)); \ handler->_rc = _z_##handler_name##_rc_new_from_val(&h); \ if (_Z_RC_IS_NULL(&handler->_rc)) { \ _z_##handler_name##_clear(&h); \ _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); \ } \ _z_##handler_name##_rc_t *h_copy = _z_##handler_name##_rc_clone_as_ptr(&handler->_rc); \ if (h_copy == NULL) { \ _z_##handler_name##_rc_drop(&handler->_rc); \ _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); \ } \ callback_new_f(callback, _z_##handler_name##_send, _z_##handler_name##_close, h_copy); \ return _Z_RES_OK; \ } \ static inline z_result_t z_##handler_name##_recv(const z_loaned_##handler_name##_t *handler, \ elem_owned_type *elem) { \ elem_null_f(elem); \ z_result_t ret = collection_pull_f(elem, (collection_type *)(&_Z_RC_IN_VAL(handler)->collection), \ _z_##handler_name##_elem_move); \ if (ret == _Z_RES_CHANNEL_CLOSED) { \ return Z_CHANNEL_DISCONNECTED; \ } \ if (ret != _Z_RES_OK) { \ _Z_ERROR("%s failed: %i", #collection_pull_f, ret); \ return ret; \ } \ return _Z_RES_OK; \ } \ static inline z_result_t z_##handler_name##_try_recv(const z_loaned_##handler_name##_t *handler, \ elem_owned_type *elem) { \ elem_null_f(elem); \ z_result_t ret = collection_try_pull_f(elem, (collection_type *)(&_Z_RC_IN_VAL(handler)->collection), \ _z_##handler_name##_elem_move); \ if (ret == _Z_RES_CHANNEL_CLOSED) { \ return Z_CHANNEL_DISCONNECTED; \ } else if (ret == _Z_RES_CHANNEL_NODATA) { \ return Z_CHANNEL_NODATA; \ } \ if (ret != _Z_RES_OK) { \ _Z_ERROR("%s failed: %i", #collection_try_pull_f, ret); \ return ret; \ } \ return _Z_RES_OK; \ } #define _Z_CHANNEL_DEFINE(item_name, kind_name) \ _Z_CHANNEL_DEFINE_IMPL(/* handler_type */ _z_##kind_name##_handler_##item_name##_t, \ /* handler_name */ kind_name##_handler_##item_name, \ /* handler_new_f_name */ z_##kind_name##_channel_##item_name##_new, \ /* callback_type */ z_owned_closure_##item_name##_t, \ /* callback_new_f */ z_closure_##item_name, \ /* collection_type */ _z_##kind_name##_mt_t, \ /* collection_new_f */ _z_##kind_name##_mt_init, \ /* collection_clear_f */ _z_##kind_name##_mt_clear, \ /* collection_push_f */ _z_##kind_name##_mt_push, \ /* collection_pull_f */ _z_##kind_name##_mt_pull, \ /* collection_try_pull_f */ _z_##kind_name##_mt_try_pull, \ /* collection_close_f */ _z_##kind_name##_mt_close, \ /* elem_owned_type */ z_owned_##item_name##_t, \ /* elem_loaned_type */ z_loaned_##item_name##_t, \ /* elem_take_f */ z_##item_name##_take_from_loaned, \ /* elem_move_f */ z_##item_name##_move, \ /* elem_drop_f */ z_##item_name##_drop, \ /* elem_null_f */ z_internal_##item_name##_null) #define _Z_CHANNEL_DUMMY_IMPL(handler_type, handler_name, item_name) \ _Z_OWNED_TYPE_VALUE(handler_type, handler_name) \ static inline void _z_##handler_name##_clear(handler_type *handler) { _ZP_UNUSED(handler); } \ static inline bool _z_##handler_name##_check(const handler_type *handler) { \ _ZP_UNUSED(handler); \ return false; \ } \ static inline handler_type _z_##handler_name##_null(void) { \ handler_type h = {0}; \ return h; \ } \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_INLINE_IMPL(handler_type, handler_name, _z_##handler_name##_check, \ _z_##handler_name##_null, _z_##handler_name##_clear) \ static inline z_result_t z_##handler_name##_try_recv(const z_loaned_##handler_name##_t *handler, \ z_owned_##item_name##_t *e) { \ _ZP_UNUSED(handler); \ _ZP_UNUSED(e); \ return Z_CHANNEL_DISCONNECTED; \ } \ static inline z_result_t z_##handler_name##_recv(const z_loaned_##handler_name##_t *handler, \ z_owned_##item_name##_t *e) { \ _ZP_UNUSED(handler); \ _ZP_UNUSED(e); \ return Z_CHANNEL_DISCONNECTED; \ } #define _Z_CHANNEL_DEFINE_DUMMY(item_name, kind_name) \ typedef struct { \ uint8_t _foo; \ } _z_##kind_name##_handler_##item_name##_t; \ _Z_CHANNEL_DUMMY_IMPL(_z_##kind_name##_handler_##item_name##_t, kind_name##_handler_##item_name, item_name) // This macro defines: // z_ring_channel_sample_new() // z_owned_ring_handler_sample_t/z_loaned_ring_handler_sample_t _Z_CHANNEL_DEFINE(sample, ring) // This macro defines: // z_fifo_channel_sample_new() // z_owned_fifo_handler_sample_t/z_loaned_fifo_handler_sample_t _Z_CHANNEL_DEFINE(sample, fifo) #if Z_FEATURE_QUERYABLE == 1 // This macro defines: // z_ring_channel_query_new() // z_owned_ring_handler_query_t/z_loaned_ring_handler_query_t _Z_CHANNEL_DEFINE(query, ring) // This macro defines: // z_fifo_channel_query_new() // z_owned_fifo_handler_query_t/z_loaned_fifo_handler_query_t _Z_CHANNEL_DEFINE(query, fifo) #else // Z_FEATURE_QUERYABLE _Z_CHANNEL_DEFINE_DUMMY(query, ring) _Z_CHANNEL_DEFINE_DUMMY(query, fifo) #endif // Z_FEATURE_QUERYABLE #if Z_FEATURE_QUERY == 1 // This macro defines: // z_ring_channel_reply_new() // z_owned_ring_handler_reply_t/z_loaned_ring_handler_reply_t _Z_CHANNEL_DEFINE(reply, ring) // This macro defines: // z_fifo_channel_reply_new() // z_owned_fifo_handler_reply_t/z_loaned_fifo_handler_reply_t _Z_CHANNEL_DEFINE(reply, fifo) #else // Z_FEATURE_QUERY _Z_CHANNEL_DEFINE_DUMMY(reply, ring) _Z_CHANNEL_DEFINE_DUMMY(reply, fifo) #endif // Z_FEATURE_QUERY #ifdef __cplusplus } #endif #endif // INCLUDE_ZENOH_PICO_API_HANDLERS_H ================================================ FILE: include/zenoh-pico/api/liveliness.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_LIVELINESS_H #define INCLUDE_ZENOH_PICO_API_LIVELINESS_H #include #include #include "olv_macros.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif typedef struct { uint32_t _id; _z_session_weak_t _zn; } _z_liveliness_token_t; _z_liveliness_token_t _z_liveliness_token_null(void); _Z_OWNED_TYPE_VALUE(_z_liveliness_token_t, liveliness_token) _Z_OWNED_FUNCTIONS_DEF(liveliness_token) #if Z_FEATURE_LIVELINESS == 1 /**************** Liveliness Token ****************/ /** * The options for :c:func:`z_liveliness_declare_token()`. */ typedef struct z_liveliness_token_options_t { uint8_t __dummy; } z_liveliness_token_options_t; /** * Constructs default value for :c:type:`z_liveliness_token_options_t`. */ z_result_t z_liveliness_token_options_default(z_liveliness_token_options_t *options); /** * Constructs and declares a liveliness token on the network. * * Liveliness token subscribers on an intersecting key expression will receive a PUT sample when connectivity * is achieved, and a DELETE sample if it's lost. * * Parameters: * zs: A Zenos session to declare the liveliness token. * token: An uninitialized memory location where liveliness token will be constructed. * keyexpr: A keyexpr to declare a liveliess token for. * options: Liveliness token declaration options. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_liveliness_declare_token(const z_loaned_session_t *zs, z_owned_liveliness_token_t *token, const z_loaned_keyexpr_t *keyexpr, const z_liveliness_token_options_t *options); /** * Undeclare a liveliness token, notifying subscribers of its destruction. * * Parameters: * token: Moved :c:type:`z_owned_liveliness_token_t` to undeclare. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_liveliness_undeclare_token(z_moved_liveliness_token_t *token); /**************** Liveliness Subscriber ****************/ #if Z_FEATURE_SUBSCRIPTION == 1 /** * The options for :c:func:`z_liveliness_declare_subscriber()` */ typedef struct z_liveliness_subscriber_options_t { bool history; } z_liveliness_subscriber_options_t; /** * Constucts default value for :c:type:`z_liveliness_subscriber_options_t`. */ z_result_t z_liveliness_subscriber_options_default(z_liveliness_subscriber_options_t *options); /** * Declares a subscriber on liveliness tokens that intersect `keyexpr`. * * Parameters: * zs: The Zenoh session. * sub: An uninitialized memory location where subscriber will be constructed. * keyexpr: The key expression to subscribe to. * callback: The callback function that will be called each time a liveliness token status is changed. * options: The options to be passed to the liveliness subscriber declaration. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_liveliness_declare_subscriber(const z_loaned_session_t *zs, z_owned_subscriber_t *sub, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options); /** * Declares a background subscriber on liveliness tokens that intersect `keyexpr`. * Subscriber callback will be called to process the messages, until the corresponding session is closed or dropped. * * Parameters: * zs: The Zenoh session. * keyexpr: The key expression to subscribe to. * callback: The callback function that will be called each time a liveliness token status is changed. * options: The options to be passed to the liveliness subscriber declaration. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. */ z_result_t z_liveliness_declare_background_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options); #endif // Z_FEATURE_SUBSCRIPTION == 1 /**************** Liveliness Query ****************/ #if Z_FEATURE_QUERY == 1 /** * The options for :c:func:`z_liveliness_get()` * * Members: * uint64_t timeout_ms: Liveliness query timeout in milliseconds. 0 corresponds to default get request timeout. * z_moved_cancellation_token_t *cancellation_token: Token to allow cancelling get operation (unstable). */ typedef struct z_liveliness_get_options_t { uint64_t timeout_ms; #ifdef Z_FEATURE_UNSTABLE_API z_moved_cancellation_token_t *cancellation_token; #endif } z_liveliness_get_options_t; /** * Constructs default value :c:type:`z_liveliness_get_options_t`. */ z_result_t z_liveliness_get_options_default(z_liveliness_get_options_t *options); /** * Queries liveliness tokens currently on the network with a key expression intersecting with `keyexpr`. * * Parameters: * zs: The Zenoh session. * keyexpr: The key expression to query liveliness tokens for. * callback: The callback function that will be called for each received reply. * options: Additional options for the liveliness get operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_liveliness_get(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_reply_t *callback, z_liveliness_get_options_t *options); #endif // Z_FEATURE_QUERY == 1 #endif // Z_FEATURE_LIVELINESS == 1 #ifdef __cplusplus } #endif #endif // INCLUDE_ZENOH_PICO_API_LIVELINESS_H ================================================ FILE: include/zenoh-pico/api/macros.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_API_MACROS_H #define ZENOH_PICO_API_MACROS_H #include "zenoh-pico/api/advanced_publisher.h" #include "zenoh-pico/api/advanced_subscriber.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/serialization.h" #include "zenoh-pico/api/types.h" #if ZENOH_C_STANDARD != 99 #ifndef __cplusplus // clang-format off #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_LOAN \ , z_owned_transport_t : z_transport_loan \ , z_owned_link_t : z_link_loan \ , z_owned_transport_event_t : z_transport_event_loan \ , z_owned_link_event_t : z_link_event_loan \ , z_owned_transport_events_listener_t : z_transport_events_listener_loan \ , z_owned_link_events_listener_t : z_link_events_listener_loan \ , z_owned_closure_transport_t : z_closure_transport_loan \ , z_owned_closure_link_t : z_closure_link_loan \ , z_owned_closure_transport_event_t : z_closure_transport_event_loan \ , z_owned_closure_link_event_t : z_closure_link_event_loan #else #define _Z_GENERIC_CONNECTIVITY_LOAN #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_LOAN_MUT \ , z_owned_transport_t : z_transport_loan_mut \ , z_owned_link_t : z_link_loan_mut \ , z_owned_transport_event_t : z_transport_event_loan_mut \ , z_owned_link_event_t : z_link_event_loan_mut \ , z_owned_transport_events_listener_t : z_transport_events_listener_loan_mut \ , z_owned_link_events_listener_t : z_link_events_listener_loan_mut #else #define _Z_GENERIC_CONNECTIVITY_LOAN_MUT #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_DROP \ , z_moved_transport_t* : z_transport_drop \ , z_moved_link_t* : z_link_drop \ , z_moved_transport_event_t* : z_transport_event_drop \ , z_moved_link_event_t* : z_link_event_drop \ , z_moved_transport_events_listener_t* : z_transport_events_listener_drop \ , z_moved_link_events_listener_t* : z_link_events_listener_drop \ , z_moved_closure_transport_t* : z_closure_transport_drop \ , z_moved_closure_link_t* : z_closure_link_drop \ , z_moved_closure_transport_event_t* : z_closure_transport_event_drop \ , z_moved_closure_link_event_t* : z_closure_link_event_drop #else #define _Z_GENERIC_CONNECTIVITY_DROP #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_CHECK \ , z_owned_transport_t : z_internal_transport_check \ , z_owned_link_t : z_internal_link_check \ , z_owned_transport_event_t : z_internal_transport_event_check \ , z_owned_link_event_t : z_internal_link_event_check \ , z_owned_transport_events_listener_t : z_internal_transport_events_listener_check \ , z_owned_link_events_listener_t : z_internal_link_events_listener_check \ , z_owned_closure_transport_t : z_internal_closure_transport_check \ , z_owned_closure_link_t : z_internal_closure_link_check \ , z_owned_closure_transport_event_t : z_internal_closure_transport_event_check \ , z_owned_closure_link_event_t : z_internal_closure_link_event_check #else #define _Z_GENERIC_CONNECTIVITY_CHECK #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_MOVE \ , z_owned_transport_t : z_transport_move \ , z_owned_link_t : z_link_move \ , z_owned_transport_event_t : z_transport_event_move \ , z_owned_link_event_t : z_link_event_move \ , z_owned_transport_events_listener_t : z_transport_events_listener_move \ , z_owned_link_events_listener_t : z_link_events_listener_move \ , z_owned_closure_transport_t : z_closure_transport_move \ , z_owned_closure_link_t : z_closure_link_move \ , z_owned_closure_transport_event_t : z_closure_transport_event_move \ , z_owned_closure_link_event_t : z_closure_link_event_move #else #define _Z_GENERIC_CONNECTIVITY_MOVE #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_TAKE \ , z_owned_transport_t *: z_transport_take \ , z_owned_link_t *: z_link_take \ , z_owned_transport_event_t *: z_transport_event_take \ , z_owned_link_event_t *: z_link_event_take \ , z_owned_transport_events_listener_t *: z_transport_events_listener_take \ , z_owned_link_events_listener_t *: z_link_events_listener_take \ , z_owned_closure_transport_t *: z_closure_transport_take \ , z_owned_closure_link_t *: z_closure_link_take \ , z_owned_closure_transport_event_t *: z_closure_transport_event_take \ , z_owned_closure_link_event_t *: z_closure_link_event_take #else #define _Z_GENERIC_CONNECTIVITY_TAKE #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_CLONE \ , z_owned_transport_t* : z_transport_clone \ , z_owned_link_t* : z_link_clone \ , z_owned_transport_event_t* : z_transport_event_clone \ , z_owned_link_event_t* : z_link_event_clone #else #define _Z_GENERIC_CONNECTIVITY_CLONE #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_TAKE_FROM_LOANED \ , z_owned_transport_t* : z_transport_take_from_loaned \ , z_owned_link_t* : z_link_take_from_loaned \ , z_owned_transport_event_t* : z_transport_event_take_from_loaned \ , z_owned_link_event_t* : z_link_event_take_from_loaned #else #define _Z_GENERIC_CONNECTIVITY_TAKE_FROM_LOANED #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_NULL \ , z_owned_transport_t * : z_internal_transport_null \ , z_owned_link_t * : z_internal_link_null \ , z_owned_transport_event_t * : z_internal_transport_event_null \ , z_owned_link_event_t * : z_internal_link_event_null \ , z_owned_transport_events_listener_t * : z_internal_transport_events_listener_null \ , z_owned_link_events_listener_t * : z_internal_link_events_listener_null \ , z_owned_closure_transport_t * : z_internal_closure_transport_null \ , z_owned_closure_link_t * : z_internal_closure_link_null \ , z_owned_closure_transport_event_t * : z_internal_closure_transport_event_null \ , z_owned_closure_link_event_t * : z_internal_closure_link_event_null #else #define _Z_GENERIC_CONNECTIVITY_NULL #endif #if Z_FEATURE_CONNECTIVITY == 1 #define _Z_GENERIC_CONNECTIVITY_CALL \ , z_loaned_closure_transport_t : z_closure_transport_call \ , z_loaned_closure_link_t : z_closure_link_call \ , z_loaned_closure_transport_event_t : z_closure_transport_event_call \ , z_loaned_closure_link_event_t : z_closure_link_event_call #else #define _Z_GENERIC_CONNECTIVITY_CALL #endif /** * Defines a generic function for loaning any of the ``z_owned_X_t`` types. * * Parameters: * x: The instance to loan. * * Returns: * Returns the loaned type associated with `x`. */ #define z_loan(x) _Generic((x), \ z_owned_keyexpr_t : z_keyexpr_loan, \ z_view_keyexpr_t : z_view_keyexpr_loan, \ z_owned_config_t : z_config_loan, \ z_owned_session_t : z_session_loan, \ z_owned_subscriber_t : z_subscriber_loan, \ z_owned_publisher_t : z_publisher_loan, \ ze_owned_advanced_subscriber_t : ze_advanced_subscriber_loan, \ ze_owned_advanced_publisher_t : ze_advanced_publisher_loan, \ z_owned_querier_t : z_querier_loan, \ z_owned_matching_listener_t : z_matching_listener_loan, \ ze_owned_sample_miss_listener_t : ze_sample_miss_listener_loan, \ z_owned_queryable_t : z_queryable_loan, \ z_owned_liveliness_token_t : z_liveliness_token_loan, \ z_owned_reply_t : z_reply_loan, \ z_owned_hello_t : z_hello_loan, \ z_owned_string_t : z_string_loan, \ z_view_string_t : z_view_string_loan, \ z_owned_string_array_t : z_string_array_loan, \ z_owned_sample_t : z_sample_loan, \ z_owned_query_t : z_query_loan, \ z_owned_slice_t : z_slice_loan, \ z_view_slice_t : z_view_slice_loan, \ z_owned_bytes_t : z_bytes_loan, \ z_owned_encoding_t : z_encoding_loan, \ z_owned_task_t : z_task_loan, \ z_owned_mutex_t : z_mutex_loan, \ z_owned_condvar_t : z_condvar_loan, \ z_owned_fifo_handler_query_t : z_fifo_handler_query_loan, \ z_owned_fifo_handler_reply_t : z_fifo_handler_reply_loan, \ z_owned_fifo_handler_sample_t : z_fifo_handler_sample_loan, \ z_owned_ring_handler_query_t : z_ring_handler_query_loan, \ z_owned_ring_handler_reply_t : z_ring_handler_reply_loan, \ z_owned_ring_handler_sample_t : z_ring_handler_sample_loan, \ z_owned_reply_err_t : z_reply_err_loan, \ z_owned_closure_sample_t : z_closure_sample_loan, \ z_owned_closure_reply_t : z_closure_reply_loan, \ z_owned_closure_query_t : z_closure_query_loan, \ z_owned_closure_hello_t : z_closure_hello_loan, \ z_owned_closure_zid_t : z_closure_zid_loan, \ z_owned_closure_matching_status_t : z_closure_matching_status_loan, \ ze_owned_closure_miss_t : ze_closure_miss_loan, \ ze_owned_serializer_t : ze_serializer_loan, \ z_owned_bytes_writer_t : z_bytes_writer_loan, \ z_owned_cancellation_token_t : z_cancellation_token_loan \ _Z_GENERIC_CONNECTIVITY_LOAN \ )(&x) #define z_loan_mut(x) _Generic((x), \ z_owned_keyexpr_t : z_keyexpr_loan_mut, \ z_owned_config_t : z_config_loan_mut, \ z_owned_session_t : z_session_loan_mut, \ z_owned_publisher_t : z_publisher_loan_mut, \ ze_owned_advanced_publisher_t : ze_advanced_publisher_loan_mut, \ ze_owned_advanced_subscriber_t : ze_advanced_subscriber_loan_mut, \ z_owned_querier_t : z_querier_loan_mut, \ z_owned_matching_listener_t : z_matching_listener_loan_mut, \ ze_owned_sample_miss_listener_t : ze_sample_miss_listener_loan_mut, \ z_owned_queryable_t : z_queryable_loan_mut, \ z_owned_liveliness_token_t : z_liveliness_token_loan_mut, \ z_owned_subscriber_t : z_subscriber_loan_mut, \ z_owned_reply_t : z_reply_loan_mut, \ z_owned_hello_t : z_hello_loan_mut, \ z_owned_string_t : z_string_loan_mut, \ z_view_string_t : z_view_string_loan_mut, \ z_owned_string_array_t : z_string_array_loan_mut, \ z_owned_sample_t : z_sample_loan_mut, \ z_owned_query_t : z_query_loan_mut, \ z_owned_slice_t : z_slice_loan_mut, \ z_view_slice_t : z_view_slice_loan_mut, \ z_owned_bytes_t : z_bytes_loan_mut, \ z_owned_task_t : z_task_loan_mut, \ z_owned_mutex_t : z_mutex_loan_mut, \ z_owned_condvar_t : z_condvar_loan_mut, \ z_owned_reply_err_t : z_reply_err_loan_mut, \ ze_owned_serializer_t : ze_serializer_loan_mut, \ z_owned_bytes_writer_t : z_bytes_writer_loan_mut, \ z_owned_cancellation_token_t : z_cancellation_token_loan_mut \ _Z_GENERIC_CONNECTIVITY_LOAN_MUT \ )(&x) /** * Defines a generic function for dropping any of the ``z_owned_X_t`` types. * * Parameters: * x: The instance to drop. */ #define z_drop(x) _Generic((x), \ z_moved_keyexpr_t* : z_keyexpr_drop, \ z_moved_config_t* : z_config_drop, \ z_moved_session_t* : z_session_drop, \ z_moved_subscriber_t* : z_subscriber_drop, \ z_moved_publisher_t* : z_publisher_drop, \ ze_moved_advanced_subscriber_t* : ze_advanced_subscriber_drop, \ ze_moved_advanced_publisher_t* : ze_advanced_publisher_drop, \ z_moved_querier_t* : z_querier_drop, \ z_moved_matching_listener_t* : z_matching_listener_drop, \ ze_moved_sample_miss_listener_t* : ze_sample_miss_listener_drop, \ z_moved_queryable_t* : z_queryable_drop, \ z_moved_liveliness_token_t* : z_liveliness_token_drop, \ z_moved_reply_t* : z_reply_drop, \ z_moved_hello_t* : z_hello_drop, \ z_moved_string_t* : z_string_drop, \ z_moved_string_array_t* : z_string_array_drop, \ z_moved_sample_t* : z_sample_drop, \ z_moved_query_t* : z_query_drop, \ z_moved_encoding_t* : z_encoding_drop, \ z_moved_slice_t* : z_slice_drop, \ z_moved_bytes_t* : z_bytes_drop, \ z_moved_closure_sample_t* : z_closure_sample_drop, \ z_moved_closure_query_t* : z_closure_query_drop, \ z_moved_closure_reply_t* : z_closure_reply_drop, \ z_moved_closure_hello_t* : z_closure_hello_drop, \ z_moved_closure_zid_t* : z_closure_zid_drop, \ z_moved_closure_matching_status_t* : z_closure_matching_status_drop, \ ze_moved_closure_miss_t* : ze_closure_miss_drop, \ z_moved_task_t* : z_task_join, \ z_moved_mutex_t* : z_mutex_drop, \ z_moved_condvar_t* : z_condvar_drop, \ z_moved_fifo_handler_query_t* : z_fifo_handler_query_drop, \ z_moved_fifo_handler_reply_t* : z_fifo_handler_reply_drop, \ z_moved_fifo_handler_sample_t* : z_fifo_handler_sample_drop, \ z_moved_ring_handler_query_t* : z_ring_handler_query_drop, \ z_moved_ring_handler_reply_t* : z_ring_handler_reply_drop, \ z_moved_ring_handler_sample_t* : z_ring_handler_sample_drop, \ z_moved_reply_err_t* : z_reply_err_drop, \ ze_moved_serializer_t* : ze_serializer_drop, \ z_moved_bytes_writer_t* : z_bytes_writer_drop, \ z_moved_cancellation_token_t* : z_cancellation_token_drop \ _Z_GENERIC_CONNECTIVITY_DROP \ )(x) /** * Defines a generic function for checking the validity of any of the ``z_owned_X_t`` types. * * Parameters: * x: The instance to check. * * Returns: * Returns ``true`` if valid, or ``false`` otherwise. */ #define z_internal_check(x) _Generic((x), \ z_owned_keyexpr_t : z_internal_keyexpr_check, \ z_owned_reply_err_t : z_internal_reply_err_check, \ z_owned_config_t : z_internal_config_check, \ z_owned_session_t : z_internal_session_check, \ z_owned_subscriber_t : z_internal_subscriber_check, \ z_owned_publisher_t : z_internal_publisher_check, \ ze_owned_advanced_subscriber_t : ze_internal_advanced_subscriber_check, \ ze_owned_advanced_publisher_t : ze_internal_advanced_publisher_check, \ z_owned_querier_t : z_internal_querier_check, \ z_owned_matching_listener_t : z_internal_matching_listener_check, \ ze_owned_sample_miss_listener_t : ze_internal_sample_miss_listener_check, \ z_owned_queryable_t : z_internal_queryable_check, \ z_owned_liveliness_token_t : z_internal_liveliness_token_check, \ z_owned_reply_t : z_internal_reply_check, \ z_owned_hello_t : z_internal_hello_check, \ z_owned_string_t : z_internal_string_check, \ z_owned_string_array_t : z_internal_string_array_check, \ z_owned_closure_sample_t : z_internal_closure_sample_check, \ z_owned_closure_query_t : z_internal_closure_query_check, \ z_owned_closure_reply_t : z_internal_closure_reply_check, \ z_owned_closure_hello_t : z_internal_closure_hello_check, \ z_owned_closure_zid_t : z_internal_closure_zid_check, \ z_owned_closure_matching_status_t : z_internal_closure_matching_status_check, \ ze_owned_closure_miss_t : ze_internal_closure_miss_check, \ z_owned_slice_t : z_internal_slice_check, \ z_owned_bytes_t : z_internal_bytes_check, \ z_owned_sample_t : z_internal_sample_check, \ z_owned_query_t : z_internal_query_check, \ z_owned_encoding_t : z_internal_encoding_check, \ ze_owned_serializer_t : ze_internal_serializer_check, \ z_owned_bytes_writer_t : z_internal_bytes_writer_check, \ z_owned_cancellation_token_t : z_internal_cancellation_token_check \ _Z_GENERIC_CONNECTIVITY_CHECK \ )(&x) /** * Defines a generic function for calling closure stored in any of ``z_owned_closure_X_t`` * * Parameters: * x: The closure to call */ #define z_call(x, ...) \ _Generic((x), z_loaned_closure_sample_t : z_closure_sample_call, \ z_loaned_closure_query_t : z_closure_query_call, \ z_loaned_closure_reply_t : z_closure_reply_call, \ z_loaned_closure_hello_t : z_closure_hello_call, \ z_loaned_closure_zid_t : z_closure_zid_call, \ z_loaned_closure_matching_status_t : z_closure_matching_status_call, \ ze_loaned_closure_miss_t : ze_closure_miss_call \ _Z_GENERIC_CONNECTIVITY_CALL \ ) (&x, __VA_ARGS__) #define z_try_recv(x, ...) \ _Generic((x), \ const z_loaned_fifo_handler_query_t* : z_fifo_handler_query_try_recv, \ const z_loaned_fifo_handler_reply_t* : z_fifo_handler_reply_try_recv, \ const z_loaned_fifo_handler_sample_t* : z_fifo_handler_sample_try_recv, \ const z_loaned_ring_handler_query_t* : z_ring_handler_query_try_recv, \ const z_loaned_ring_handler_reply_t* : z_ring_handler_reply_try_recv, \ const z_loaned_ring_handler_sample_t* : z_ring_handler_sample_try_recv \ )(x, __VA_ARGS__) #define z_recv(x, ...) \ _Generic((x), \ const z_loaned_fifo_handler_query_t* : z_fifo_handler_query_recv, \ const z_loaned_fifo_handler_reply_t* : z_fifo_handler_reply_recv, \ const z_loaned_fifo_handler_sample_t* : z_fifo_handler_sample_recv, \ const z_loaned_ring_handler_query_t* : z_ring_handler_query_recv, \ const z_loaned_ring_handler_reply_t* : z_ring_handler_reply_recv, \ const z_loaned_ring_handler_sample_t* : z_ring_handler_sample_recv \ )(x, __VA_ARGS__) /** * Defines a generic function for moving any of the ``z_owned_X_t`` types. * * Parameters: * x: The instance to move. * * Returns: * Returns the instance associated with `x`. */ #define z_move(x) _Generic((x), \ z_owned_keyexpr_t : z_keyexpr_move, \ z_owned_config_t : z_config_move, \ z_owned_session_t : z_session_move, \ z_owned_subscriber_t : z_subscriber_move, \ z_owned_publisher_t : z_publisher_move, \ ze_owned_advanced_subscriber_t : ze_advanced_subscriber_move, \ ze_owned_advanced_publisher_t : ze_advanced_publisher_move, \ z_owned_querier_t : z_querier_move, \ z_owned_matching_listener_t: z_matching_listener_move, \ ze_owned_sample_miss_listener_t: ze_sample_miss_listener_move, \ z_owned_queryable_t : z_queryable_move, \ z_owned_liveliness_token_t : z_liveliness_token_move, \ z_owned_reply_t : z_reply_move, \ z_owned_hello_t : z_hello_move, \ z_owned_string_t : z_string_move, \ z_owned_string_array_t : z_string_array_move, \ z_owned_closure_sample_t : z_closure_sample_move, \ z_owned_closure_query_t : z_closure_query_move, \ z_owned_closure_reply_t : z_closure_reply_move, \ z_owned_closure_hello_t : z_closure_hello_move, \ z_owned_closure_zid_t : z_closure_zid_move, \ z_owned_closure_matching_status_t : z_closure_matching_status_move, \ ze_owned_closure_miss_t : ze_closure_miss_move, \ z_owned_sample_t : z_sample_move, \ z_owned_query_t : z_query_move, \ z_owned_slice_t : z_slice_move, \ z_owned_bytes_t : z_bytes_move, \ z_owned_encoding_t : z_encoding_move, \ z_owned_task_t : z_task_move, \ z_owned_mutex_t : z_mutex_move, \ z_owned_condvar_t : z_condvar_move, \ z_owned_ring_handler_query_t : z_ring_handler_query_move, \ z_owned_ring_handler_reply_t : z_ring_handler_reply_move, \ z_owned_ring_handler_sample_t : z_ring_handler_sample_move, \ z_owned_fifo_handler_query_t : z_fifo_handler_query_move, \ z_owned_fifo_handler_reply_t : z_fifo_handler_reply_move, \ z_owned_fifo_handler_sample_t : z_fifo_handler_sample_move, \ z_owned_reply_err_t : z_reply_err_move, \ ze_owned_serializer_t : ze_serializer_move, \ z_owned_bytes_writer_t : z_bytes_writer_move, \ z_owned_cancellation_token_t : z_cancellation_token_move \ _Z_GENERIC_CONNECTIVITY_MOVE \ )(&x) /** * Defines a generic function for extracting the ``z_owned_X_t`` type from ``z_moved_X_t`` * * Parameters: * x: The pointer to destinaton ``z_owned_X_t`` * src: The source ``z_moved_X_t`` * * Returns: * Returns the instance associated with `x`. */ #define z_take(this_, x) \ _Generic((this_), \ z_owned_bytes_t *: z_bytes_take, \ z_owned_closure_hello_t *: z_closure_hello_take, \ z_owned_closure_query_t *: z_closure_query_take, \ z_owned_closure_reply_t *: z_closure_reply_take, \ z_owned_closure_sample_t *: z_closure_sample_take, \ z_owned_closure_zid_t * : z_closure_zid_take, \ z_owned_closure_matching_status_t * : z_closure_matching_status_take, \ ze_owned_closure_miss_t * : ze_closure_miss_take, \ z_owned_condvar_t *: z_condvar_take, \ z_owned_config_t *: z_config_take, \ z_owned_encoding_t *: z_encoding_take, \ z_owned_fifo_handler_query_t *: z_fifo_handler_query_take, \ z_owned_fifo_handler_reply_t *: z_fifo_handler_reply_take, \ z_owned_fifo_handler_sample_t *: z_fifo_handler_sample_take, \ z_owned_hello_t *: z_hello_take, \ z_owned_keyexpr_t *: z_keyexpr_take, \ z_owned_mutex_t *: z_mutex_take, \ z_owned_publisher_t *: z_publisher_take, \ ze_owned_advanced_publisher_t *: ze_advanced_publisher_take, \ z_owned_querier_t *: z_querier_take, \ z_owned_matching_listener_t *: z_matching_listener_take, \ ze_owned_sample_miss_listener_t *: ze_sample_miss_listener_take, \ z_owned_query_t *: z_query_take, \ z_owned_queryable_t *: z_queryable_take, \ z_owned_liveliness_token_t *: z_liveliness_token_take, \ z_owned_reply_t *: z_reply_take, \ z_owned_reply_err_t *: z_reply_err_take, \ z_owned_ring_handler_query_t *: z_ring_handler_query_take, \ z_owned_ring_handler_reply_t *: z_ring_handler_reply_take, \ z_owned_ring_handler_sample_t *: z_ring_handler_sample_take, \ z_owned_sample_t *: z_sample_take, \ z_owned_session_t *: z_session_take, \ z_owned_slice_t *: z_slice_take, \ z_owned_string_array_t *: z_string_array_take, \ z_owned_string_t *: z_string_take, \ z_owned_subscriber_t *: z_subscriber_take, \ ze_owned_serializer_t *: ze_serializer_take, \ z_owned_bytes_writer_t *: z_bytes_writer_take, \ z_owned_cancellation_token_t *: z_cancellation_token_take \ _Z_GENERIC_CONNECTIVITY_TAKE \ )(this_, x) /** * Defines a generic function for cloning of the ``z_owned_X_t`` types. * * Parameters: * dst: The clone destination. * src: The instance to clone. * * Returns: * `0` in case of success, negative error code otherwise. */ #define z_clone(dst, src) _Generic((dst), \ z_owned_keyexpr_t* : z_keyexpr_clone, \ z_owned_query_t* : z_query_clone, \ z_owned_sample_t* : z_sample_clone, \ z_owned_bytes_t* : z_bytes_clone, \ z_owned_encoding_t* : z_encoding_clone, \ z_owned_reply_err_t* : z_reply_err_clone, \ z_owned_reply_t* : z_reply_clone, \ z_owned_hello_t* : z_hello_clone, \ z_owned_string_t* : z_string_clone, \ z_owned_slice_t* : z_slice_clone, \ z_owned_string_array_t* : z_string_array_clone, \ z_owned_config_t* : z_config_clone, \ z_owned_cancellation_token_t* : z_cancellation_token_clone \ _Z_GENERIC_CONNECTIVITY_CLONE \ )(dst, src) /** * Defines a generic function for moving of the ``z_owned_X_t`` types. * * Parameters: * dst: The take destination. * src: The instance to take contents from. * * Returns: * `0` in case of success, negative error code otherwise. */ #define z_take_from_loaned(dst, src) _Generic((dst), \ z_owned_keyexpr_t* : z_keyexpr_take_from_loaned, \ z_owned_query_t* : z_query_take_from_loaned, \ z_owned_sample_t* : z_sample_take_from_loaned, \ z_owned_bytes_t* : z_bytes_take_from_loaned, \ z_owned_encoding_t* : z_encoding_take_from_loaned, \ z_owned_reply_err_t* : z_reply_err_take_from_loaned, \ z_owned_reply_t* : z_reply_take_from_loaned, \ z_owned_hello_t* : z_hello_take_from_loaned, \ z_owned_string_t* : z_string_take_from_loaned, \ z_owned_slice_t* : z_slice_take_from_loaned, \ z_owned_string_array_t* : z_string_array_take_from_loaned, \ z_owned_config_t* : z_config_take_from_loaned, \ z_owned_bytes_writer_t* : z_bytes_writer_take_from_loaned, \ ze_owned_serializer_t* : ze_serializer_take_from_loaned, \ z_cancellation_token_t* : z_cancellation_token_take_from_loaned \ _Z_GENERIC_CONNECTIVITY_TAKE_FROM_LOANED \ )(dst, src) /** * Defines a generic function for making null object of any of the ``z_owned_X_t`` types. * * Parameters: * x: The instance to nullify. */ #define z_internal_null(x) _Generic((x), \ z_owned_session_t * : z_internal_session_null, \ z_owned_publisher_t * : z_internal_publisher_null, \ ze_owned_advanced_subscriber_t * : ze_internal_advanced_subscriber_null, \ ze_owned_advanced_publisher_t * : ze_internal_advanced_publisher_null, \ z_owned_querier_t * : z_internal_querier_null, \ z_owned_matching_listener_t * : z_internal_matching_listener_null, \ ze_owned_sample_miss_listener_t * : ze_internal_sample_miss_listener_null, \ z_owned_keyexpr_t * : z_internal_keyexpr_null, \ z_owned_config_t * : z_internal_config_null, \ z_owned_subscriber_t * : z_internal_subscriber_null, \ z_owned_queryable_t * : z_internal_queryable_null, \ z_owned_liveliness_token_t * : z_internal_liveliness_token_null, \ z_owned_query_t * : z_internal_query_null, \ z_owned_reply_t * : z_internal_reply_null, \ z_owned_hello_t * : z_internal_hello_null, \ z_owned_string_t * : z_internal_string_null, \ z_owned_string_array_t * : z_internal_string_array_null, \ z_owned_slice_t *: z_internal_slice_null, \ z_owned_bytes_t *: z_internal_bytes_null, \ z_owned_closure_sample_t * : z_internal_closure_sample_null, \ z_owned_closure_query_t * : z_internal_closure_query_null, \ z_owned_closure_reply_t * : z_internal_closure_reply_null, \ z_owned_closure_hello_t * : z_internal_closure_hello_null, \ z_owned_closure_zid_t * : z_internal_closure_zid_null, \ z_owned_closure_matching_status_t * : z_internal_closure_matching_status_null, \ ze_owned_closure_miss_t * : ze_internal_closure_miss_null, \ z_owned_sample_t * : z_internal_sample_null, \ z_owned_encoding_t * : z_internal_encoding_null, \ z_owned_reply_err_t * : z_internal_reply_err_null, \ ze_owned_serializer_t * : ze_internal_serializer_null, \ z_owned_bytes_writer_t * : z_internal_bytes_writer_null, \ z_owned_cancellation_token_t * : z_internal_cancellation_token_null \ _Z_GENERIC_CONNECTIVITY_NULL \ )(x) // clang-format on #define _z_closure_overloader(closure, callback, dropper, ctx, ...) \ do { \ (closure)->_val.call = callback; \ (closure)->_val.drop = dropper; \ (closure)->_val.context = ctx; \ } while (0); /** * Defines a variadic macro to ease the definition of callback closures. * * Parameters: * callback: the typical ``callback`` function. ``context`` will be passed as its last argument. * dropper: allows the callback's state to be freed. ``context`` will be passed as its last argument. * context: a pointer to an arbitrary state. * * Returns: * Returns the new closure. */ #define z_closure(...) _z_closure_overloader(__VA_ARGS__, 0, 0, 0) #else // __cplusplus // clang-format off // z_loan definition inline const z_loaned_keyexpr_t* z_loan(const z_owned_keyexpr_t& x) { return z_keyexpr_loan(&x); } inline const z_loaned_keyexpr_t* z_loan(const z_view_keyexpr_t& x) { return z_view_keyexpr_loan(&x); } inline const z_loaned_config_t* z_loan(const z_owned_config_t& x) { return z_config_loan(&x); } inline const z_loaned_session_t* z_loan(const z_owned_session_t& x) { return z_session_loan(&x); } inline const z_loaned_subscriber_t* z_loan(const z_owned_subscriber_t& x) { return z_subscriber_loan(&x); } inline const z_loaned_publisher_t* z_loan(const z_owned_publisher_t& x) { return z_publisher_loan(&x); } inline const ze_loaned_advanced_subscriber_t* z_loan(const ze_owned_advanced_subscriber_t& x) { return ze_advanced_subscriber_loan(&x); } inline const ze_loaned_advanced_publisher_t* z_loan(const ze_owned_advanced_publisher_t& x) { return ze_advanced_publisher_loan(&x); } inline const z_loaned_querier_t* z_loan(const z_owned_querier_t& x) { return z_querier_loan(&x); } inline const z_loaned_matching_listener_t* z_loan(const z_owned_matching_listener_t& x) { return z_matching_listener_loan(&x); } inline const ze_loaned_sample_miss_listener_t* z_loan(const ze_owned_sample_miss_listener_t& x) { return ze_sample_miss_listener_loan(&x); } inline const z_loaned_queryable_t* z_loan(const z_owned_queryable_t& x) { return z_queryable_loan(&x); } inline const z_loaned_liveliness_token_t* z_loan(const z_owned_liveliness_token_t& x) { return z_liveliness_token_loan(&x); } inline const z_loaned_reply_t* z_loan(const z_owned_reply_t& x) { return z_reply_loan(&x); } inline const z_loaned_hello_t* z_loan(const z_owned_hello_t& x) { return z_hello_loan(&x); } inline const z_loaned_string_t* z_loan(const z_owned_string_t& x) { return z_string_loan(&x); } inline const z_loaned_string_t* z_loan(const z_view_string_t& x) { return z_view_string_loan(&x); } inline const z_loaned_string_array_t* z_loan(const z_owned_string_array_t& x) { return z_string_array_loan(&x); } inline const z_loaned_sample_t* z_loan(const z_owned_sample_t& x) { return z_sample_loan(&x); } inline const z_loaned_query_t* z_loan(const z_owned_query_t& x) { return z_query_loan(&x); } inline const z_loaned_slice_t* z_loan(const z_owned_slice_t& x) { return z_slice_loan(&x); } inline const z_loaned_slice_t* z_loan(const z_view_slice_t& x) { return z_view_slice_loan(&x); } inline const z_loaned_bytes_t* z_loan(const z_owned_bytes_t& x) { return z_bytes_loan(&x); } inline const z_loaned_encoding_t* z_loan(const z_owned_encoding_t& x) { return z_encoding_loan(&x); } inline const z_loaned_task_t* z_loan(const z_owned_task_t& x) { return z_task_loan(&x); } inline const z_loaned_mutex_t* z_loan(const z_owned_mutex_t& x) { return z_mutex_loan(&x); } inline const z_loaned_condvar_t* z_loan(const z_owned_condvar_t& x) { return z_condvar_loan(&x); } inline const z_loaned_reply_err_t* z_loan(const z_owned_reply_err_t& x) { return z_reply_err_loan(&x); } inline const z_loaned_closure_sample_t* z_loan(const z_owned_closure_sample_t& x) { return z_closure_sample_loan(&x); } inline const z_loaned_closure_reply_t* z_loan(const z_owned_closure_reply_t& x) { return z_closure_reply_loan(&x); } inline const z_loaned_closure_query_t* z_loan(const z_owned_closure_query_t& x) { return z_closure_query_loan(&x); } inline const z_loaned_closure_hello_t* z_loan(const z_owned_closure_hello_t& x) { return z_closure_hello_loan(&x); } inline const z_loaned_closure_zid_t* z_loan(const z_owned_closure_zid_t& x) { return z_closure_zid_loan(&x); } inline const z_loaned_closure_matching_status_t* z_loan(const z_owned_closure_matching_status_t& x) { return z_closure_matching_status_loan(&x); } inline const ze_loaned_closure_miss_t* z_loan(const ze_owned_closure_miss_t& x) { return ze_closure_miss_loan(&x); } inline const z_loaned_fifo_handler_query_t* z_loan(const z_owned_fifo_handler_query_t& x) { return z_fifo_handler_query_loan(&x); } inline const z_loaned_fifo_handler_reply_t* z_loan(const z_owned_fifo_handler_reply_t& x) { return z_fifo_handler_reply_loan(&x); } inline const z_loaned_fifo_handler_sample_t* z_loan(const z_owned_fifo_handler_sample_t& x) { return z_fifo_handler_sample_loan(&x); } inline const z_loaned_ring_handler_query_t* z_loan(const z_owned_ring_handler_query_t& x) { return z_ring_handler_query_loan(&x); } inline const z_loaned_ring_handler_reply_t* z_loan(const z_owned_ring_handler_reply_t& x) { return z_ring_handler_reply_loan(&x); } inline const z_loaned_ring_handler_sample_t* z_loan(const z_owned_ring_handler_sample_t& x) { return z_ring_handler_sample_loan(&x); } inline const z_loaned_bytes_writer_t* z_loan(const z_owned_bytes_writer_t& x) { return z_bytes_writer_loan(&x); } inline const ze_loaned_serializer_t* z_loan(const ze_owned_serializer_t& x) { return ze_serializer_loan(&x); } inline const z_loaned_cancellation_token_t* z_loan(const z_owned_cancellation_token_t& x) { return z_cancellation_token_loan(&x); } #if Z_FEATURE_CONNECTIVITY == 1 inline const z_loaned_transport_t* z_loan(const z_owned_transport_t& x) { return z_transport_loan(&x); } inline const z_loaned_link_t* z_loan(const z_owned_link_t& x) { return z_link_loan(&x); } inline const z_loaned_transport_event_t* z_loan(const z_owned_transport_event_t& x) { return z_transport_event_loan(&x); } inline const z_loaned_link_event_t* z_loan(const z_owned_link_event_t& x) { return z_link_event_loan(&x); } inline const z_loaned_transport_events_listener_t* z_loan(const z_owned_transport_events_listener_t& x) { return z_transport_events_listener_loan(&x); } inline const z_loaned_link_events_listener_t* z_loan(const z_owned_link_events_listener_t& x) { return z_link_events_listener_loan(&x); } inline const z_loaned_closure_transport_t* z_loan(const z_owned_closure_transport_t& x) { return z_closure_transport_loan(&x); } inline const z_loaned_closure_link_t* z_loan(const z_owned_closure_link_t& x) { return z_closure_link_loan(&x); } inline const z_loaned_closure_transport_event_t* z_loan(const z_owned_closure_transport_event_t& x) { return z_closure_transport_event_loan(&x); } inline const z_loaned_closure_link_event_t* z_loan(const z_owned_closure_link_event_t& x) { return z_closure_link_event_loan(&x); } #endif // z_loan_mut definition inline z_loaned_keyexpr_t* z_loan_mut(z_owned_keyexpr_t& x) { return z_keyexpr_loan_mut(&x); } inline z_loaned_keyexpr_t* z_loan_mut(z_view_keyexpr_t& x) { return z_view_keyexpr_loan_mut(&x); } inline z_loaned_config_t* z_loan_mut(z_owned_config_t& x) { return z_config_loan_mut(&x); } inline z_loaned_session_t* z_loan_mut(z_owned_session_t& x) { return z_session_loan_mut(&x); } inline z_loaned_publisher_t* z_loan_mut(z_owned_publisher_t& x) { return z_publisher_loan_mut(&x); } inline ze_loaned_advanced_publisher_t* z_loan_mut(ze_owned_advanced_publisher_t& x) { return ze_advanced_publisher_loan_mut(&x); } inline z_loaned_querier_t* z_loan_mut(z_owned_querier_t& x) { return z_querier_loan_mut(&x); } inline z_loaned_matching_listener_t* z_loan_mut(z_owned_matching_listener_t& x) { return z_matching_listener_loan_mut(&x); } inline ze_loaned_sample_miss_listener_t* z_loan_mut(ze_owned_sample_miss_listener_t& x) { return ze_sample_miss_listener_loan_mut(&x); } inline z_loaned_queryable_t* z_loan_mut(z_owned_queryable_t& x) { return z_queryable_loan_mut(&x); } inline z_loaned_liveliness_token_t* z_loan_mut(z_owned_liveliness_token_t& x) { return z_liveliness_token_loan_mut(&x); } inline z_loaned_subscriber_t* z_loan_mut(z_owned_subscriber_t& x) { return z_subscriber_loan_mut(&x); } inline ze_loaned_advanced_subscriber_t* z_loan_mut(ze_owned_advanced_subscriber_t& x) { return ze_advanced_subscriber_loan_mut(&x); } inline z_loaned_reply_t* z_loan_mut(z_owned_reply_t& x) { return z_reply_loan_mut(&x); } inline z_loaned_hello_t* z_loan_mut(z_owned_hello_t& x) { return z_hello_loan_mut(&x); } inline z_loaned_string_t* z_loan_mut(z_owned_string_t& x) { return z_string_loan_mut(&x); } inline z_loaned_string_t* z_loan_mut(z_view_string_t& x) { return z_view_string_loan_mut(&x); } inline z_loaned_slice_t* z_loan_mut(z_view_slice_t& x) { return z_view_slice_loan_mut(&x); } inline z_loaned_string_array_t* z_loan_mut(z_owned_string_array_t& x) { return z_string_array_loan_mut(&x); } inline z_loaned_sample_t* z_loan_mut(z_owned_sample_t& x) { return z_sample_loan_mut(&x); } inline z_loaned_query_t* z_loan_mut(z_owned_query_t& x) { return z_query_loan_mut(&x); } inline z_loaned_slice_t* z_loan_mut(z_owned_slice_t& x) { return z_slice_loan_mut(&x); } inline z_loaned_bytes_t* z_loan_mut(z_owned_bytes_t& x) { return z_bytes_loan_mut(&x); } inline z_loaned_encoding_t* z_loan_mut(z_owned_encoding_t& x) { return z_encoding_loan_mut(&x); } inline z_loaned_task_t* z_loan_mut(z_owned_task_t& x) { return z_task_loan_mut(&x); } inline z_loaned_mutex_t* z_loan_mut(z_owned_mutex_t& x) { return z_mutex_loan_mut(&x); } inline z_loaned_condvar_t* z_loan_mut(z_owned_condvar_t& x) { return z_condvar_loan_mut(&x); } inline z_loaned_reply_err_t* z_loan_mut(z_owned_reply_err_t& x) { return z_reply_err_loan_mut(&x); } inline z_loaned_bytes_writer_t* z_loan_mut(z_owned_bytes_writer_t& x) { return z_bytes_writer_loan_mut(&x); } inline ze_loaned_serializer_t* z_loan_mut(ze_owned_serializer_t& x) { return ze_serializer_loan_mut(&x); } inline z_loaned_cancellation_token_t* z_loan_mut(z_owned_cancellation_token_t& x) { return z_cancellation_token_loan_mut(&x); } #if Z_FEATURE_CONNECTIVITY == 1 inline z_loaned_transport_t* z_loan_mut(z_owned_transport_t& x) { return z_transport_loan_mut(&x); } inline z_loaned_link_t* z_loan_mut(z_owned_link_t& x) { return z_link_loan_mut(&x); } inline z_loaned_transport_event_t* z_loan_mut(z_owned_transport_event_t& x) { return z_transport_event_loan_mut(&x); } inline z_loaned_link_event_t* z_loan_mut(z_owned_link_event_t& x) { return z_link_event_loan_mut(&x); } inline z_loaned_transport_events_listener_t* z_loan_mut(z_owned_transport_events_listener_t& x) { return z_transport_events_listener_loan_mut(&x); } inline z_loaned_link_events_listener_t* z_loan_mut(z_owned_link_events_listener_t& x) { return z_link_events_listener_loan_mut(&x); } #endif // z_drop definition inline void z_drop(z_moved_session_t* v) { z_session_drop(v); } inline void z_drop(z_moved_publisher_t* v) { z_publisher_drop(v); } inline void z_drop(ze_moved_advanced_publisher_t* v) { ze_advanced_publisher_drop(v); } inline void z_drop(z_moved_querier_t* v) { z_querier_drop(v); } inline void z_drop(z_moved_matching_listener_t* v) { z_matching_listener_drop(v); } inline void z_drop(ze_moved_sample_miss_listener_t* v) { ze_sample_miss_listener_drop(v); } inline void z_drop(z_moved_keyexpr_t* v) { z_keyexpr_drop(v); } inline void z_drop(z_moved_config_t* v) { z_config_drop(v); } inline void z_drop(z_moved_subscriber_t* v) { z_subscriber_drop(v); } inline void z_drop(ze_moved_advanced_subscriber_t* v) { ze_advanced_subscriber_drop(v); } inline void z_drop(z_moved_queryable_t* v) { z_queryable_drop(v); } inline void z_drop(z_moved_liveliness_token_t* v) { z_liveliness_token_drop(v); } inline void z_drop(z_moved_reply_t* v) { z_reply_drop(v); } inline void z_drop(z_moved_hello_t* v) { z_hello_drop(v); } inline void z_drop(z_moved_string_t* v) { z_string_drop(v); } inline void z_drop(z_moved_slice_t* v) { z_slice_drop(v); } inline void z_drop(z_moved_string_array_t* v) { z_string_array_drop(v); } inline void z_drop(z_moved_sample_t* v) { z_sample_drop(v); } inline void z_drop(z_moved_query_t* v) { z_query_drop(v); } inline void z_drop(z_moved_bytes_t* v) { z_bytes_drop(v); } inline void z_drop(z_moved_encoding_t* v) { z_encoding_drop(v); } inline void z_drop(z_moved_mutex_t* v) { z_mutex_drop(v); } inline void z_drop(z_moved_condvar_t* v) { z_condvar_drop(v); } inline void z_drop(z_moved_reply_err_t* v) { z_reply_err_drop(v); } inline void z_drop(z_moved_closure_sample_t* v) { z_closure_sample_drop(v); } inline void z_drop(z_moved_closure_query_t* v) { z_closure_query_drop(v); } inline void z_drop(z_moved_closure_reply_t* v) { z_closure_reply_drop(v); } inline void z_drop(z_moved_closure_hello_t* v) { z_closure_hello_drop(v); } inline void z_drop(z_moved_closure_zid_t* v) { z_closure_zid_drop(v); } inline void z_drop(z_moved_closure_matching_status_t* v) { z_closure_matching_status_drop(v); } inline void z_drop(ze_moved_closure_miss_t* v) { ze_closure_miss_drop(v); } inline void z_drop(z_moved_ring_handler_sample_t* v) { z_ring_handler_sample_drop(v); } inline void z_drop(z_moved_fifo_handler_sample_t* v) { z_fifo_handler_sample_drop(v); } inline void z_drop(z_moved_ring_handler_query_t* v) { z_ring_handler_query_drop(v); } inline void z_drop(z_moved_fifo_handler_query_t* v) { z_fifo_handler_query_drop(v); } inline void z_drop(z_moved_ring_handler_reply_t* v) { z_ring_handler_reply_drop(v); } inline void z_drop(z_moved_fifo_handler_reply_t* v) { z_fifo_handler_reply_drop(v); } inline void z_drop(z_moved_bytes_writer_t* v) { z_bytes_writer_drop(v); } inline void z_drop(ze_moved_serializer_t* v) { ze_serializer_drop(v); } inline void z_drop(z_moved_cancellation_token_t* v) { z_cancellation_token_drop(v); } #if Z_FEATURE_CONNECTIVITY == 1 inline void z_drop(z_moved_transport_t* v) { z_transport_drop(v); } inline void z_drop(z_moved_link_t* v) { z_link_drop(v); } inline void z_drop(z_moved_transport_event_t* v) { z_transport_event_drop(v); } inline void z_drop(z_moved_link_event_t* v) { z_link_event_drop(v); } inline void z_drop(z_moved_transport_events_listener_t* v) { z_transport_events_listener_drop(v); } inline void z_drop(z_moved_link_events_listener_t* v) { z_link_events_listener_drop(v); } inline void z_drop(z_moved_closure_transport_t* v) { z_closure_transport_drop(v); } inline void z_drop(z_moved_closure_link_t* v) { z_closure_link_drop(v); } inline void z_drop(z_moved_closure_transport_event_t* v) { z_closure_transport_event_drop(v); } inline void z_drop(z_moved_closure_link_event_t* v) { z_closure_link_event_drop(v); } #endif // z_internal_null definition inline void z_internal_null(z_owned_session_t* v) { z_internal_session_null(v); } inline void z_internal_null(z_owned_publisher_t* v) { z_internal_publisher_null(v); } inline void z_internal_null(ze_owned_advanced_publisher_t* v) { ze_internal_advanced_publisher_null(v); } inline void z_internal_null(z_owned_querier_t* v) { z_internal_querier_null(v); } inline void z_internal_null(z_owned_matching_listener_t* v) { z_internal_matching_listener_null(v); } inline void z_internal_null(ze_owned_sample_miss_listener_t* v) { ze_internal_sample_miss_listener_null(v); } inline void z_internal_null(z_owned_keyexpr_t* v) { z_internal_keyexpr_null(v); } inline void z_internal_null(z_owned_config_t* v) { z_internal_config_null(v); } inline void z_internal_null(z_owned_subscriber_t* v) { z_internal_subscriber_null(v); } inline void z_internal_null(ze_owned_advanced_subscriber_t* v) { ze_internal_advanced_subscriber_null(v); } inline void z_internal_null(z_owned_queryable_t* v) { z_internal_queryable_null(v); } inline void z_internal_null(z_owned_liveliness_token_t* v) { z_internal_liveliness_token_null(v); } inline void z_internal_null(z_owned_query_t* v) { z_internal_query_null(v); } inline void z_internal_null(z_owned_sample_t* v) { z_internal_sample_null(v); } inline void z_internal_null(z_owned_reply_t* v) { z_internal_reply_null(v); } inline void z_internal_null(z_owned_hello_t* v) { z_internal_hello_null(v); } inline void z_internal_null(z_owned_string_t* v) { z_internal_string_null(v); } inline void z_internal_null(z_owned_bytes_t* v) { z_internal_bytes_null(v); } inline void z_internal_null(z_owned_encoding_t* v) { z_internal_encoding_null(v); } inline void z_internal_null(z_owned_reply_err_t* v) { z_internal_reply_err_null(v); } inline void z_internal_null(z_owned_closure_sample_t* v) { z_internal_closure_sample_null(v); } inline void z_internal_null(z_owned_closure_query_t* v) { z_internal_closure_query_null(v); } inline void z_internal_null(z_owned_closure_reply_t* v) { z_internal_closure_reply_null(v); } inline void z_internal_null(z_owned_closure_hello_t* v) { z_internal_closure_hello_null(v); } inline void z_internal_null(z_owned_closure_zid_t* v) { z_internal_closure_zid_null(v); } inline void z_internal_null(z_owned_closure_matching_status_t* v) { z_internal_closure_matching_status_null(v); } inline void z_internal_null(ze_owned_closure_miss_t* v) { ze_internal_closure_miss_null(v); } inline void z_internal_null(z_owned_ring_handler_query_t* v) { return z_internal_ring_handler_query_null(v); } inline void z_internal_null(z_owned_ring_handler_reply_t* v) { return z_internal_ring_handler_reply_null(v); } inline void z_internal_null(z_owned_ring_handler_sample_t* v) { return z_internal_ring_handler_sample_null(v); } inline void z_internal_null(z_owned_fifo_handler_query_t* v) { return z_internal_fifo_handler_query_null(v); } inline void z_internal_null(z_owned_fifo_handler_reply_t* v) { return z_internal_fifo_handler_reply_null(v); } inline void z_internal_null(z_owned_fifo_handler_sample_t* v) { return z_internal_fifo_handler_sample_null(v); } inline void z_internal_null(z_owned_bytes_writer_t* v) { return z_internal_bytes_writer_null(v); } inline void z_internal_null(ze_owned_serializer_t* v) { return ze_internal_serializer_null(v); } inline void z_internal_null(z_owned_cancellation_token_t* v) { return z_internal_cancellation_token_null(v); } #if Z_FEATURE_CONNECTIVITY == 1 inline void z_internal_null(z_owned_transport_t* v) { z_internal_transport_null(v); } inline void z_internal_null(z_owned_link_t* v) { z_internal_link_null(v); } inline void z_internal_null(z_owned_transport_event_t* v) { z_internal_transport_event_null(v); } inline void z_internal_null(z_owned_link_event_t* v) { z_internal_link_event_null(v); } inline void z_internal_null(z_owned_transport_events_listener_t* v) { z_internal_transport_events_listener_null(v); } inline void z_internal_null(z_owned_link_events_listener_t* v) { z_internal_link_events_listener_null(v); } inline void z_internal_null(z_owned_closure_transport_t* v) { z_internal_closure_transport_null(v); } inline void z_internal_null(z_owned_closure_link_t* v) { z_internal_closure_link_null(v); } inline void z_internal_null(z_owned_closure_transport_event_t* v) { z_internal_closure_transport_event_null(v); } inline void z_internal_null(z_owned_closure_link_event_t* v) { z_internal_closure_link_event_null(v); } #endif // z_internal_check definition inline bool z_internal_check(const z_owned_session_t& v) { return z_internal_session_check(&v); } inline bool z_internal_check(const z_owned_publisher_t& v) { return z_internal_publisher_check(&v); } inline bool z_internal_check(const ze_owned_advanced_publisher_t& v) { return ze_internal_advanced_publisher_check(&v); } inline bool z_internal_check(const z_owned_querier_t& v) { return z_internal_querier_check(&v); } inline bool z_internal_check(const z_owned_matching_listener_t& v) { return z_internal_matching_listener_check(&v); } inline bool z_internal_check(const ze_owned_sample_miss_listener_t& v) { return ze_internal_sample_miss_listener_check(&v); } inline bool z_internal_check(const z_owned_keyexpr_t& v) { return z_internal_keyexpr_check(&v); } inline bool z_internal_check(const z_owned_config_t& v) { return z_internal_config_check(&v); } inline bool z_internal_check(const z_owned_subscriber_t& v) { return z_internal_subscriber_check(&v); } inline bool z_internal_check(const ze_owned_advanced_subscriber_t& v) { return ze_internal_advanced_subscriber_check(&v); } inline bool z_internal_check(const z_owned_queryable_t& v) { return z_internal_queryable_check(&v); } inline bool z_internal_check(const z_owned_liveliness_token_t& v) { return z_internal_liveliness_token_check(&v); } inline bool z_internal_check(const z_owned_reply_t& v) { return z_internal_reply_check(&v); } inline bool z_internal_check(const z_owned_query_t& v) { return z_internal_query_check(&v); } inline bool z_internal_check(const z_owned_hello_t& v) { return z_internal_hello_check(&v); } inline bool z_internal_check(const z_owned_string_t& v) { return z_internal_string_check(&v); } inline bool z_internal_check(const z_owned_sample_t& v) { return z_internal_sample_check(&v); } inline bool z_internal_check(const z_owned_bytes_t& v) { return z_internal_bytes_check(&v); } inline bool z_internal_check(const z_owned_encoding_t& v) { return z_internal_encoding_check(&v); } inline bool z_internal_check(const z_owned_reply_err_t& v) { return z_internal_reply_err_check(&v); } inline bool z_internal_check(const z_owned_fifo_handler_query_t& v) { return z_internal_fifo_handler_query_check(&v); } inline bool z_internal_check(const z_owned_fifo_handler_reply_t& v) { return z_internal_fifo_handler_reply_check(&v); } inline bool z_internal_check(const z_owned_fifo_handler_sample_t& v) { return z_internal_fifo_handler_sample_check(&v); } inline bool z_internal_check(const z_owned_ring_handler_query_t& v) { return z_internal_ring_handler_query_check(&v); } inline bool z_internal_check(const z_owned_ring_handler_reply_t& v) { return z_internal_ring_handler_reply_check(&v); } inline bool z_internal_check(const z_owned_ring_handler_sample_t& v) { return z_internal_ring_handler_sample_check(&v); } inline bool z_internal_check(const z_owned_bytes_writer_t& v) { return z_internal_bytes_writer_check(&v); } inline bool z_internal_check(const ze_owned_serializer_t& v) { return ze_internal_serializer_check(&v); } inline bool z_internal_check(const z_owned_cancellation_token_t& v) { return z_internal_cancellation_token_check(&v); } #if Z_FEATURE_CONNECTIVITY == 1 inline bool z_internal_check(const z_owned_transport_t& v) { return z_internal_transport_check(&v); } inline bool z_internal_check(const z_owned_link_t& v) { return z_internal_link_check(&v); } inline bool z_internal_check(const z_owned_transport_event_t& v) { return z_internal_transport_event_check(&v); } inline bool z_internal_check(const z_owned_link_event_t& v) { return z_internal_link_event_check(&v); } inline bool z_internal_check(const z_owned_transport_events_listener_t& v) { return z_internal_transport_events_listener_check(&v); } inline bool z_internal_check(const z_owned_link_events_listener_t& v) { return z_internal_link_events_listener_check(&v); } inline bool z_internal_check(const z_owned_closure_transport_t& v) { return z_internal_closure_transport_check(&v); } inline bool z_internal_check(const z_owned_closure_link_t& v) { return z_internal_closure_link_check(&v); } inline bool z_internal_check(const z_owned_closure_transport_event_t& v) { return z_internal_closure_transport_event_check(&v); } inline bool z_internal_check(const z_owned_closure_link_event_t& v) { return z_internal_closure_link_event_check(&v); } #endif // z_call definition inline void z_call(const z_loaned_closure_sample_t &closure, z_loaned_sample_t *sample) { z_closure_sample_call(&closure, sample); } inline void z_call(const z_loaned_closure_query_t &closure, z_loaned_query_t *query) { z_closure_query_call(&closure, query); } inline void z_call(const z_loaned_closure_reply_t &closure, z_loaned_reply_t *reply) { z_closure_reply_call(&closure, reply); } inline void z_call(const z_loaned_closure_hello_t &closure, z_loaned_hello_t *hello) { z_closure_hello_call(&closure, hello); } inline void z_call(const z_loaned_closure_zid_t &closure, const z_id_t *zid) { z_closure_zid_call(&closure, zid); } inline void z_call(const z_loaned_closure_matching_status_t &closure, const z_matching_status_t *status) { z_closure_matching_status_call(&closure, status); } inline void z_call(const ze_loaned_closure_miss_t &closure, const ze_miss_t *miss) { ze_closure_miss_call(&closure, miss); } #if Z_FEATURE_CONNECTIVITY == 1 inline void z_call(const z_loaned_closure_transport_t &closure, z_loaned_transport_t *transport) { z_closure_transport_call(&closure, transport); } inline void z_call(const z_loaned_closure_link_t &closure, z_loaned_link_t *link) { z_closure_link_call(&closure, link); } inline void z_call(const z_loaned_closure_transport_event_t &closure, z_loaned_transport_event_t *event) { z_closure_transport_event_call(&closure, event); } inline void z_call(const z_loaned_closure_link_event_t &closure, z_loaned_link_event_t *event) { z_closure_link_event_call(&closure, event); } #endif inline void z_closure( z_owned_closure_hello_t* closure, void (*call)(z_loaned_hello_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_query_t* closure, void (*call)(z_loaned_query_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_reply_t* closure, void (*call)(z_loaned_reply_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_sample_t* closure, void (*call)(z_loaned_sample_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_zid_t* closure, void (*call)(const z_id_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_matching_status_t* closure, void (*call)(const z_matching_status_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( ze_owned_closure_miss_t* closure, void (*call)(const ze_miss_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } #if Z_FEATURE_CONNECTIVITY == 1 inline void z_closure( z_owned_closure_transport_t* closure, void (*call)(z_loaned_transport_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_link_t* closure, void (*call)(z_loaned_link_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_transport_event_t* closure, void (*call)(z_loaned_transport_event_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } inline void z_closure( z_owned_closure_link_event_t* closure, void (*call)(z_loaned_link_event_t*, void*), void (*drop)(void*), void *context) { closure->_val.context = context; closure->_val.drop = drop; closure->_val.call = call; } #endif inline z_result_t z_try_recv(const z_loaned_fifo_handler_query_t* this_, z_owned_query_t* query) { return z_fifo_handler_query_try_recv(this_, query); } inline z_result_t z_try_recv(const z_loaned_fifo_handler_reply_t* this_, z_owned_reply_t* reply) { return z_fifo_handler_reply_try_recv(this_, reply); } inline z_result_t z_try_recv(const z_loaned_fifo_handler_sample_t* this_, z_owned_sample_t* sample) { return z_fifo_handler_sample_try_recv(this_, sample); } inline z_result_t z_try_recv(const z_loaned_ring_handler_query_t* this_, z_owned_query_t* query) { return z_ring_handler_query_try_recv(this_, query); } inline z_result_t z_try_recv(const z_loaned_ring_handler_reply_t* this_, z_owned_reply_t* reply) { return z_ring_handler_reply_try_recv(this_, reply); } inline z_result_t z_try_recv(const z_loaned_ring_handler_sample_t* this_, z_owned_sample_t* sample) { return z_ring_handler_sample_try_recv(this_, sample); } inline z_result_t z_recv(const z_loaned_fifo_handler_query_t* this_, z_owned_query_t* query) { return z_fifo_handler_query_recv(this_, query); } inline z_result_t z_recv(const z_loaned_fifo_handler_reply_t* this_, z_owned_reply_t* reply) { return z_fifo_handler_reply_recv(this_, reply); } inline z_result_t z_recv(const z_loaned_fifo_handler_sample_t* this_, z_owned_sample_t* sample) { return z_fifo_handler_sample_recv(this_, sample); } inline z_result_t z_recv(const z_loaned_ring_handler_query_t* this_, z_owned_query_t* query) { return z_ring_handler_query_recv(this_, query); } inline z_result_t z_recv(const z_loaned_ring_handler_reply_t* this_, z_owned_reply_t* reply) { return z_ring_handler_reply_recv(this_, reply); } inline z_result_t z_recv(const z_loaned_ring_handler_sample_t* this_, z_owned_sample_t* sample) { return z_ring_handler_sample_recv(this_, sample); } // clang-format on inline z_moved_bytes_t* z_move(z_owned_bytes_t& x) { return z_bytes_move(&x); } inline z_moved_closure_hello_t* z_move(z_owned_closure_hello_t& closure) { return z_closure_hello_move(&closure); } inline z_moved_closure_query_t* z_move(z_owned_closure_query_t& closure) { return z_closure_query_move(&closure); } inline z_moved_closure_reply_t* z_move(z_owned_closure_reply_t& closure) { return z_closure_reply_move(&closure); } inline z_moved_closure_sample_t* z_move(z_owned_closure_sample_t& closure) { return z_closure_sample_move(&closure); } inline z_moved_closure_zid_t* z_move(z_owned_closure_zid_t& closure) { return z_closure_zid_move(&closure); } inline z_moved_closure_matching_status_t* z_move(z_owned_closure_matching_status_t& closure) { return z_closure_matching_status_move(&closure); } inline ze_moved_closure_miss_t* z_move(ze_owned_closure_miss_t& closure) { return ze_closure_miss_move(&closure); } inline z_moved_config_t* z_move(z_owned_config_t& x) { return z_config_move(&x); } inline z_moved_encoding_t* z_move(z_owned_encoding_t& x) { return z_encoding_move(&x); } inline z_moved_reply_err_t* z_move(z_owned_reply_err_t& x) { return z_reply_err_move(&x); } inline z_moved_hello_t* z_move(z_owned_hello_t& x) { return z_hello_move(&x); } inline z_moved_keyexpr_t* z_move(z_owned_keyexpr_t& x) { return z_keyexpr_move(&x); } inline z_moved_publisher_t* z_move(z_owned_publisher_t& x) { return z_publisher_move(&x); } inline ze_moved_advanced_publisher_t* z_move(ze_owned_advanced_publisher_t& x) { return ze_advanced_publisher_move(&x); } inline z_moved_querier_t* z_move(z_owned_querier_t& x) { return z_querier_move(&x); } inline z_moved_matching_listener_t* z_move(z_owned_matching_listener_t& x) { return z_matching_listener_move(&x); } inline ze_moved_sample_miss_listener_t* z_move(ze_owned_sample_miss_listener_t& x) { return ze_sample_miss_listener_move(&x); } inline z_moved_query_t* z_move(z_owned_query_t& x) { return z_query_move(&x); } inline z_moved_queryable_t* z_move(z_owned_queryable_t& x) { return z_queryable_move(&x); } inline z_moved_liveliness_token_t* z_move(z_owned_liveliness_token_t& x) { return z_liveliness_token_move(&x); } inline z_moved_reply_t* z_move(z_owned_reply_t& x) { return z_reply_move(&x); } inline z_moved_sample_t* z_move(z_owned_sample_t& x) { return z_sample_move(&x); } inline z_moved_session_t* z_move(z_owned_session_t& x) { return z_session_move(&x); } inline z_moved_slice_t* z_move(z_owned_slice_t& x) { return z_slice_move(&x); } inline z_moved_string_array_t* z_move(z_owned_string_array_t& x) { return z_string_array_move(&x); } inline z_moved_string_t* z_move(z_owned_string_t& x) { return z_string_move(&x); } inline z_moved_subscriber_t* z_move(z_owned_subscriber_t& x) { return z_subscriber_move(&x); } inline ze_moved_advanced_subscriber_t* z_move(ze_owned_advanced_subscriber_t& x) { return ze_advanced_subscriber_move(&x); } inline z_moved_fifo_handler_query_t* z_move(z_owned_fifo_handler_query_t& x) { return z_fifo_handler_query_move(&x); } inline z_moved_fifo_handler_reply_t* z_move(z_owned_fifo_handler_reply_t& x) { return z_fifo_handler_reply_move(&x); } inline z_moved_fifo_handler_sample_t* z_move(z_owned_fifo_handler_sample_t& x) { return z_fifo_handler_sample_move(&x); } inline z_moved_ring_handler_query_t* z_move(z_owned_ring_handler_query_t& x) { return z_ring_handler_query_move(&x); } inline z_moved_ring_handler_reply_t* z_move(z_owned_ring_handler_reply_t& x) { return z_ring_handler_reply_move(&x); } inline z_moved_ring_handler_sample_t* z_move(z_owned_ring_handler_sample_t& x) { return z_ring_handler_sample_move(&x); } inline z_moved_bytes_writer_t* z_move(z_owned_bytes_writer_t& x) { return z_bytes_writer_move(&x); } inline ze_moved_serializer_t* z_move(ze_owned_serializer_t& x) { return ze_serializer_move(&x); } inline z_moved_cancellation_token_t* z_move(z_owned_cancellation_token_t& x) { return z_cancellation_token_move(&x); } #if Z_FEATURE_CONNECTIVITY == 1 inline z_moved_transport_t* z_move(z_owned_transport_t& x) { return z_transport_move(&x); } inline z_moved_link_t* z_move(z_owned_link_t& x) { return z_link_move(&x); } inline z_moved_transport_event_t* z_move(z_owned_transport_event_t& x) { return z_transport_event_move(&x); } inline z_moved_link_event_t* z_move(z_owned_link_event_t& x) { return z_link_event_move(&x); } inline z_moved_transport_events_listener_t* z_move(z_owned_transport_events_listener_t& x) { return z_transport_events_listener_move(&x); } inline z_moved_link_events_listener_t* z_move(z_owned_link_events_listener_t& x) { return z_link_events_listener_move(&x); } inline z_moved_closure_transport_t* z_move(z_owned_closure_transport_t& x) { return z_closure_transport_move(&x); } inline z_moved_closure_link_t* z_move(z_owned_closure_link_t& x) { return z_closure_link_move(&x); } inline z_moved_closure_transport_event_t* z_move(z_owned_closure_transport_event_t& x) { return z_closure_transport_event_move(&x); } inline z_moved_closure_link_event_t* z_move(z_owned_closure_link_event_t& x) { return z_closure_link_event_move(&x); } #endif // z_take definition inline void z_take(z_owned_session_t* this_, z_moved_session_t* v) { return z_session_take(this_, v); } inline void z_take(z_owned_publisher_t* this_, z_moved_publisher_t* v) { return z_publisher_take(this_, v); } inline void z_take(ze_owned_advanced_publisher_t* this_, ze_moved_advanced_publisher_t* v) { return ze_advanced_publisher_take(this_, v); } inline void z_take(z_owned_querier_t* this_, z_moved_querier_t* v) { return z_querier_take(this_, v); } inline void z_take(z_owned_matching_listener_t* this_, z_moved_matching_listener_t* v) { return z_matching_listener_take(this_, v); } inline void z_take(ze_owned_sample_miss_listener_t* this_, ze_moved_sample_miss_listener_t* v) { return ze_sample_miss_listener_take(this_, v); } inline void z_take(z_owned_keyexpr_t* this_, z_moved_keyexpr_t* v) { z_keyexpr_take(this_, v); } inline void z_take(z_owned_config_t* this_, z_moved_config_t* v) { z_config_take(this_, v); } inline void z_take(z_owned_subscriber_t* this_, z_moved_subscriber_t* v) { return z_subscriber_take(this_, v); } inline void z_take(ze_owned_advanced_subscriber_t* this_, ze_moved_advanced_subscriber_t* v) { return ze_advanced_subscriber_take(this_, v); } inline void z_take(z_owned_queryable_t* this_, z_moved_queryable_t* v) { return z_queryable_take(this_, v); } inline void z_take(z_owned_liveliness_token_t* this_, z_moved_liveliness_token_t* v) { return z_liveliness_token_take(this_, v); } inline void z_take(z_owned_reply_t* this_, z_moved_reply_t* v) { z_reply_take(this_, v); } inline void z_take(z_owned_hello_t* this_, z_moved_hello_t* v) { z_hello_take(this_, v); } inline void z_take(z_owned_string_t* this_, z_moved_string_t* v) { z_string_take(this_, v); } inline void z_take(z_owned_slice_t* this_, z_moved_slice_t* v) { z_slice_take(this_, v); } inline void z_take(z_owned_string_array_t* this_, z_moved_string_array_t* v) { z_string_array_take(this_, v); } inline void z_take(z_owned_sample_t* this_, z_moved_sample_t* v) { z_sample_take(this_, v); } inline void z_take(z_owned_query_t* this_, z_moved_query_t* v) { z_query_take(this_, v); } inline void z_take(z_owned_bytes_t* this_, z_moved_bytes_t* v) { z_bytes_take(this_, v); } inline void z_take(z_owned_encoding_t* this_, z_moved_encoding_t* v) { z_encoding_take(this_, v); } inline void z_take(z_owned_mutex_t* this_, z_moved_mutex_t* v) { z_mutex_take(this_, v); } inline void z_take(z_owned_condvar_t* this_, z_moved_condvar_t* v) { z_condvar_take(this_, v); } inline void z_take(z_owned_reply_err_t* this_, z_moved_reply_err_t* v) { z_reply_err_take(this_, v); } inline void z_take(z_owned_closure_sample_t* this_, z_moved_closure_sample_t* v) { z_closure_sample_take(this_, v); } inline void z_take(z_owned_closure_query_t* this_, z_moved_closure_query_t* v) { z_closure_query_take(this_, v); } inline void z_take(z_owned_closure_reply_t* this_, z_moved_closure_reply_t* v) { z_closure_reply_take(this_, v); } inline void z_take(z_owned_closure_hello_t* this_, z_moved_closure_hello_t* v) { z_closure_hello_take(this_, v); } inline void z_take(z_owned_closure_zid_t* this_, z_moved_closure_zid_t* v) { z_closure_zid_take(this_, v); } inline void z_take(z_owned_closure_matching_status_t* this_, z_moved_closure_matching_status_t* v) { z_closure_matching_status_take(this_, v); } inline void z_take(ze_owned_closure_miss_t* this_, ze_moved_closure_miss_t* v) { ze_closure_miss_take(this_, v); } inline void z_take(z_owned_ring_handler_sample_t* this_, z_moved_ring_handler_sample_t* v) { z_ring_handler_sample_take(this_, v); } inline void z_take(z_owned_fifo_handler_sample_t* this_, z_moved_fifo_handler_sample_t* v) { z_fifo_handler_sample_take(this_, v); } inline void z_take(z_owned_ring_handler_query_t* this_, z_moved_ring_handler_query_t* v) { z_ring_handler_query_take(this_, v); } inline void z_take(z_owned_fifo_handler_query_t* this_, z_moved_fifo_handler_query_t* v) { z_fifo_handler_query_take(this_, v); } inline void z_take(z_owned_ring_handler_reply_t* this_, z_moved_ring_handler_reply_t* v) { z_ring_handler_reply_take(this_, v); } inline void z_take(z_owned_fifo_handler_reply_t* this_, z_moved_fifo_handler_reply_t* v) { z_fifo_handler_reply_take(this_, v); } inline void z_take(z_owned_bytes_writer_t* this_, z_moved_bytes_writer_t* v) { z_bytes_writer_take(this_, v); } inline void z_take(ze_owned_serializer_t* this_, ze_moved_serializer_t* v) { ze_serializer_take(this_, v); } inline void z_take(z_owned_cancellation_token_t* this_, z_moved_cancellation_token_t* v) { z_cancellation_token_take(this_, v); } #if Z_FEATURE_CONNECTIVITY == 1 inline void z_take(z_owned_transport_t* this_, z_moved_transport_t* v) { z_transport_take(this_, v); } inline void z_take(z_owned_link_t* this_, z_moved_link_t* v) { z_link_take(this_, v); } inline void z_take(z_owned_transport_event_t* this_, z_moved_transport_event_t* v) { z_transport_event_take(this_, v); } inline void z_take(z_owned_link_event_t* this_, z_moved_link_event_t* v) { z_link_event_take(this_, v); } inline void z_take(z_owned_transport_events_listener_t* this_, z_moved_transport_events_listener_t* v) { z_transport_events_listener_take(this_, v); } inline void z_take(z_owned_link_events_listener_t* this_, z_moved_link_events_listener_t* v) { z_link_events_listener_take(this_, v); } inline void z_take(z_owned_closure_transport_t* this_, z_moved_closure_transport_t* v) { z_closure_transport_take(this_, v); } inline void z_take(z_owned_closure_link_t* this_, z_moved_closure_link_t* v) { z_closure_link_take(this_, v); } inline void z_take(z_owned_closure_transport_event_t* this_, z_moved_closure_transport_event_t* v) { z_closure_transport_event_take(this_, v); } inline void z_take(z_owned_closure_link_event_t* this_, z_moved_closure_link_event_t* v) { z_closure_link_event_take(this_, v); } #endif // z_clone definition inline z_result_t z_clone(z_owned_bytes_t* dst, const z_loaned_bytes_t* this_) { return z_bytes_clone(dst, this_); } inline z_result_t z_clone(z_owned_config_t* dst, const z_loaned_config_t* this_) { return z_config_clone(dst, this_); } inline z_result_t z_clone(z_owned_encoding_t* dst, const z_loaned_encoding_t* this_) { return z_encoding_clone(dst, this_); } inline z_result_t z_clone(z_owned_keyexpr_t* dst, const z_loaned_keyexpr_t* this_) { return z_keyexpr_clone(dst, this_); } inline z_result_t z_clone(z_owned_query_t* dst, const z_loaned_query_t* this_) { return z_query_clone(dst, this_); } inline z_result_t z_clone(z_owned_reply_t* dst, const z_loaned_reply_t* this_) { return z_reply_clone(dst, this_); } inline z_result_t z_clone(z_owned_reply_err_t* dst, const z_loaned_reply_err_t* this_) { return z_reply_err_clone(dst, this_); } inline z_result_t z_clone(z_owned_sample_t* dst, const z_loaned_sample_t* this_) { return z_sample_clone(dst, this_); } inline z_result_t z_clone(z_owned_slice_t* dst, const z_loaned_slice_t* this_) { return z_slice_clone(dst, this_); } inline z_result_t z_clone(z_owned_string_t* dst, const z_loaned_string_t* this_) { return z_string_clone(dst, this_); } inline z_result_t z_clone(z_owned_string_array_t* dst, const z_loaned_string_array_t* this_) { return z_string_array_clone(dst, this_); } inline z_result_t z_clone(z_owned_hello_t* dst, const z_loaned_hello_t* this_) { return z_hello_clone(dst, this_); } #if Z_FEATURE_CONNECTIVITY == 1 inline z_result_t z_clone(z_owned_transport_t* dst, const z_loaned_transport_t* this_) { return z_transport_clone(dst, this_); } inline z_result_t z_clone(z_owned_link_t* dst, const z_loaned_link_t* this_) { return z_link_clone(dst, this_); } inline z_result_t z_clone(z_owned_transport_event_t* dst, const z_loaned_transport_event_t* this_) { return z_transport_event_clone(dst, this_); } inline z_result_t z_clone(z_owned_link_event_t* dst, const z_loaned_link_event_t* this_) { return z_link_event_clone(dst, this_); } #endif // z_take_from_loaned definition inline z_result_t z_take_from_loaned(z_owned_bytes_t* dst, z_loaned_bytes_t* this_) { return z_bytes_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_config_t* dst, z_loaned_config_t* this_) { return z_config_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_encoding_t* dst, z_loaned_encoding_t* this_) { return z_encoding_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_keyexpr_t* dst, z_loaned_keyexpr_t* this_) { return z_keyexpr_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_query_t* dst, z_loaned_query_t* this_) { return z_query_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_reply_t* dst, z_loaned_reply_t* this_) { return z_reply_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_reply_err_t* dst, z_loaned_reply_err_t* this_) { return z_reply_err_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_sample_t* dst, z_loaned_sample_t* this_) { return z_sample_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_slice_t* dst, z_loaned_slice_t* this_) { return z_slice_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_string_t* dst, z_loaned_string_t* this_) { return z_string_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_string_array_t* dst, z_loaned_string_array_t* this_) { return z_string_array_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_hello_t* dst, z_loaned_hello_t* this_) { return z_hello_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_bytes_writer_t* dst, z_loaned_bytes_writer_t* this_) { return z_bytes_writer_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(ze_owned_serializer_t* dst, ze_loaned_serializer_t* this_) { return ze_serializer_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_cancellation_token_t* dst, z_loaned_cancellation_token_t* this_) { return z_cancellation_token_take_from_loaned(dst, this_); } #if Z_FEATURE_CONNECTIVITY == 1 inline z_result_t z_take_from_loaned(z_owned_transport_t* dst, z_loaned_transport_t* this_) { return z_transport_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_link_t* dst, z_loaned_link_t* this_) { return z_link_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_transport_event_t* dst, z_loaned_transport_event_t* this_) { return z_transport_event_take_from_loaned(dst, this_); } inline z_result_t z_take_from_loaned(z_owned_link_event_t* dst, z_loaned_link_event_t* this_) { return z_link_event_take_from_loaned(dst, this_); } #endif template struct z_loaned_to_owned_type_t {}; template struct z_owned_to_loaned_type_t {}; template <> struct z_loaned_to_owned_type_t { typedef z_owned_bytes_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_bytes_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_config_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_config_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_encoding_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_encoding_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_reply_err_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_reply_err_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_hello_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_hello_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_keyexpr_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_keyexpr_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_publisher_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_publisher_t type; }; template <> struct z_loaned_to_owned_type_t { typedef ze_owned_advanced_publisher_t type; }; template <> struct z_owned_to_loaned_type_t { typedef ze_loaned_advanced_publisher_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_querier_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_querier_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_matching_listener_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_matching_listener_t type; }; template <> struct z_loaned_to_owned_type_t { typedef ze_owned_sample_miss_listener_t type; }; template <> struct z_owned_to_loaned_type_t { typedef ze_loaned_sample_miss_listener_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_query_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_query_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_queryable_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_queryable_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_liveliness_token_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_liveliness_token_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_reply_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_reply_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_sample_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_sample_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_session_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_session_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_slice_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_slice_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_string_array_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_string_array_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_string_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_string_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_subscriber_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_subscriber_t type; }; template <> struct z_loaned_to_owned_type_t { typedef ze_owned_advanced_subscriber_t type; }; template <> struct z_owned_to_loaned_type_t { typedef ze_loaned_advanced_subscriber_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_sample_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_sample_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_reply_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_reply_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_query_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_query_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_hello_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_hello_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_zid_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_zid_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_matching_status_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_matching_status_t type; }; template <> struct z_owned_to_loaned_type_t { typedef ze_loaned_closure_miss_t type; }; template <> struct z_loaned_to_owned_type_t { typedef ze_owned_closure_miss_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_fifo_handler_query_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_fifo_handler_query_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_fifo_handler_reply_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_fifo_handler_reply_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_fifo_handler_sample_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_fifo_handler_sample_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_ring_handler_query_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_ring_handler_query_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_ring_handler_reply_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_ring_handler_reply_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_ring_handler_sample_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_ring_handler_sample_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_bytes_writer_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_bytes_writer_t type; }; template <> struct z_loaned_to_owned_type_t { typedef ze_owned_serializer_t type; }; template <> struct z_owned_to_loaned_type_t { typedef ze_loaned_serializer_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_cancellation_token_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_cancellation_token_t type; }; #if Z_FEATURE_CONNECTIVITY == 1 template <> struct z_loaned_to_owned_type_t { typedef z_owned_transport_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_transport_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_link_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_link_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_transport_event_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_transport_event_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_link_event_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_link_event_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_transport_events_listener_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_transport_events_listener_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_link_events_listener_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_link_events_listener_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_transport_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_transport_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_link_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_link_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_transport_event_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_transport_event_t type; }; template <> struct z_loaned_to_owned_type_t { typedef z_owned_closure_link_event_t type; }; template <> struct z_owned_to_loaned_type_t { typedef z_loaned_closure_link_event_t type; }; #endif #endif #endif /* ZENOH_C_STANDARD != 99 */ #endif /* ZENOH_PICO_API_MACROS_H */ ================================================ FILE: include/zenoh-pico/api/olv_macros.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_OLV_MACROS_H #define INCLUDE_ZENOH_PICO_API_OLV_MACROS_H // Gets internal value from refcounted type (e.g. z_loaned_session_t, z_query_t) #define _Z_RC_IN_VAL(arg) ((arg)->_val) // Checks if refcounted type is initialized #define _Z_RC_IS_NULL(arg) ((arg)->_cnt == NULL) // Gets internal value from refcounted owned type (e.g. z_owned_session_t, z_owned_query_t) #define _Z_OWNED_RC_IN_VAL(arg) ((arg)->_rc._val) // Checks if simple refcounted type is initialized #define _Z_SIMPLE_RC_IS_NULL(arg) ((arg)->_val == NULL) // Owned/Loaned/View type macros // // !!! FOR INTERNAL USAGE ONLY !!! #define _Z_MOVED_TYPE_PREFIX(prefix, name) \ typedef struct { \ prefix##_owned_##name##_t _this; \ } prefix##_moved_##name##_t; #define _Z_LOANED_TYPE_PREFIX(prefix, type, name) typedef type prefix##_loaned_##name##_t; // For value types #define _Z_OWNED_TYPE_VALUE_PREFIX(prefix, type, name) \ typedef struct { \ type _val; \ } prefix##_owned_##name##_t; \ _Z_LOANED_TYPE_PREFIX(prefix, type, name) \ _Z_MOVED_TYPE_PREFIX(prefix, name) #define _Z_OWNED_TYPE_VALUE(type, name) _Z_OWNED_TYPE_VALUE_PREFIX(z, type, name) // For refcounted types #define _Z_OWNED_TYPE_RC_PREFIX(prefix, type, name) \ typedef struct { \ type _rc; \ } prefix##_owned_##name##_t; \ _Z_LOANED_TYPE_PREFIX(prefix, type, name) \ _Z_MOVED_TYPE_PREFIX(prefix, name) #define _Z_OWNED_TYPE_RC(type, name) _Z_OWNED_TYPE_RC_PREFIX(z, type, name) #define _Z_VIEW_TYPE(type, name) \ typedef struct { \ type _val; \ } z_view_##name##_t; #define _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(prefix, name) \ void prefix##_internal_##name##_null(prefix##_owned_##name##_t *obj); \ bool prefix##_internal_##name##_check(const prefix##_owned_##name##_t *obj); \ const prefix##_loaned_##name##_t *prefix##_##name##_loan(const prefix##_owned_##name##_t *obj); \ prefix##_loaned_##name##_t *prefix##_##name##_loan_mut(prefix##_owned_##name##_t *obj); \ prefix##_moved_##name##_t *prefix##_##name##_move(prefix##_owned_##name##_t *obj); \ void prefix##_##name##_take(prefix##_owned_##name##_t *obj, prefix##_moved_##name##_t *src); \ void prefix##_##name##_drop(prefix##_moved_##name##_t *obj); #define _Z_OWNED_FUNCTIONS_NO_COPY_DEF_PREFIX(prefix, name) \ _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(prefix, name) \ z_result_t prefix##_##name##_take_from_loaned(prefix##_owned_##name##_t *dst, prefix##_loaned_##name##_t *src); #define _Z_OWNED_FUNCTIONS_DEF_PREFIX(prefix, name) \ _Z_OWNED_FUNCTIONS_NO_COPY_DEF_PREFIX(prefix, name) \ z_result_t prefix##_##name##_clone(prefix##_owned_##name##_t *obj, const prefix##_loaned_##name##_t *src); #define _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(name) _Z_OWNED_FUNCTIONS_NO_COPY_NO_TAKE_FROM_LOANED_DEF_PREFIX(z, name) #define _Z_OWNED_FUNCTIONS_NO_COPY_DEF(name) _Z_OWNED_FUNCTIONS_NO_COPY_DEF_PREFIX(z, name) #define _Z_OWNED_FUNCTIONS_DEF(name) _Z_OWNED_FUNCTIONS_DEF_PREFIX(z, name) #define _Z_OWNED_FUNCTIONS_SYSTEM_DEF(name) \ void z_internal_##name##_null(z_owned_##name##_t *obj); \ void z_##name##_take(z_owned_##name##_t *obj, z_moved_##name##_t *src); \ const z_loaned_##name##_t *z_##name##_loan(const z_owned_##name##_t *obj); \ z_loaned_##name##_t *z_##name##_loan_mut(z_owned_##name##_t *obj); \ z_moved_##name##_t *z_##name##_move(z_owned_##name##_t *obj); #define _Z_VIEW_FUNCTIONS_DEF(name) \ bool z_view_##name##_is_empty(const z_view_##name##_t *obj); \ const z_loaned_##name##_t *z_view_##name##_loan(const z_view_##name##_t *name); \ z_loaned_##name##_t *z_view_##name##_loan_mut(z_view_##name##_t *name); \ void z_view_##name##_empty(z_view_##name##_t *name); #define _ZP_NOTHING #define _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(prefix, name, attribute) \ attribute prefix##_moved_##name##_t *prefix##_##name##_move(prefix##_owned_##name##_t *obj) { \ return (prefix##_moved_##name##_t *)(obj); \ } \ attribute void prefix##_##name##_take(prefix##_owned_##name##_t *obj, prefix##_moved_##name##_t *src) { \ *obj = src->_this; \ prefix##_internal_##name##_null(&src->_this); \ } #define _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE(name) _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(z, name, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX(prefix, name) \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(prefix, name, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_drop, \ attribute) \ attribute void prefix##_internal_##name##_null(prefix##_owned_##name##_t *obj) { obj->_val = f_null(); } \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(prefix, name, attribute) \ attribute bool prefix##_internal_##name##_check(const prefix##_owned_##name##_t *obj) { \ return f_check((&obj->_val)); \ } \ attribute const prefix##_loaned_##name##_t *prefix##_##name##_loan(const prefix##_owned_##name##_t *obj) { \ return &obj->_val; \ } \ attribute prefix##_loaned_##name##_t *prefix##_##name##_loan_mut(prefix##_owned_##name##_t *obj) { \ return &obj->_val; \ } \ attribute void prefix##_##name##_drop(prefix##_moved_##name##_t *obj) { \ if (obj != NULL) f_drop((&obj->_this._val)); \ } #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_move, f_drop, \ attribute) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_drop, attribute) \ attribute z_result_t prefix##_##name##_take_from_loaned(prefix##_owned_##name##_t *obj, \ prefix##_loaned_##name##_t *src) { \ return f_move((&obj->_val), src); \ } #define _Z_OWNED_FUNCTIONS_VALUE_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_copy, f_move, f_drop, \ attribute) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_move, f_drop, attribute) \ attribute z_result_t prefix##_##name##_clone(prefix##_owned_##name##_t *obj, \ const prefix##_loaned_##name##_t *src) { \ return f_copy((&obj->_val), src); \ } #define _Z_OWNED_FUNCTIONS_VALUE_IMP_PREFIX(prefix, type, name, f_check, f_null, f_copy, f_move, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_IMPL_PREFIX_INNER(z, type, name, f_check, f_null, f_copy, f_move, f_drop, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX(prefix, type, name, f_check, f_null, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_drop, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX(prefix, type, name, f_check, f_null, f_move, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX_INNER(prefix, type, name, f_check, f_null, f_move, f_drop, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(type, name, f_check, f_null, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX(z, type, name, f_check, f_null, f_drop) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL(type, name, f_check, f_null, f_move, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX(z, type, name, f_check, f_null, f_move, f_drop) #define _Z_OWNED_FUNCTIONS_VALUE_IMPL(type, name, f_check, f_null, f_copy, f_move, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_IMP_PREFIX(z, type, name, f_check, f_null, f_copy, f_move, f_drop) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_INLINE_IMPL(type, name, f_check, f_null, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX_INNER(z, type, name, f_check, f_null, f_drop, static inline) #define _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_INLINE_IMPL(type, name, f_check, f_null, f_move, f_drop) \ _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX_INNER(z, type, name, f_check, f_null, f_move, f_drop, static inline) #define _Z_OWNED_FUNCTIONS_RC_IMPL_CLONE_DROP_INNER(name, attribute) \ attribute z_result_t z_##name##_clone(z_owned_##name##_t *obj, const z_loaned_##name##_t *src) { \ z_result_t ret = _Z_RES_OK; \ obj->_rc = _z_##name##_rc_clone((z_loaned_##name##_t *)src); \ if (_Z_RC_IS_NULL(&obj->_rc)) { \ _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); \ ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; \ } \ return ret; \ } \ attribute void z_##name##_drop(z_moved_##name##_t *obj) { \ if (obj != NULL && !_Z_RC_IS_NULL(&obj->_this._rc)) { \ _z_##name##_rc_drop(&obj->_this._rc); \ } \ } #define _Z_OWNED_FUNCTIONS_RC_IMPL_NULL_CHECK_LOAN_INNER(name, attribute) \ attribute void z_internal_##name##_null(z_owned_##name##_t *val) { val->_rc = _z_##name##_rc_null(); } \ attribute bool z_internal_##name##_check(const z_owned_##name##_t *val) { return !_Z_RC_IS_NULL(&val->_rc); } \ attribute const z_loaned_##name##_t *z_##name##_loan(const z_owned_##name##_t *val) { return &val->_rc; } \ attribute z_loaned_##name##_t *z_##name##_loan_mut(z_owned_##name##_t *val) { return &val->_rc; } #define _Z_OWNED_FUNCTIONS_RC_IMPL_INNER(name, attribute) \ _Z_OWNED_FUNCTIONS_RC_IMPL_NULL_CHECK_LOAN_INNER(name, attribute) \ _Z_OWNED_FUNCTIONS_RC_IMPL_CLONE_DROP_INNER(name, attribute) \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(z, name, attribute) #define _Z_OWNED_FUNCTIONS_RC_IMPL_NO_DROP_CLONE_INNER(name, attribute) \ _Z_OWNED_FUNCTIONS_RC_IMPL_NULL_CHECK_LOAN_INNER(name, attribute) \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX_INNER(z, name, attribute) #define _Z_OWNED_FUNCTIONS_RC_IMPL(name) _Z_OWNED_FUNCTIONS_RC_IMPL_INNER(name, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_RC_INLINE_IMPL(name) _Z_OWNED_FUNCTIONS_RC_IMPL_INNER(name, static inline) #define _Z_OWNED_FUNCTIONS_RC_IMPL_NO_DROP_CLONE(name) _Z_OWNED_FUNCTIONS_RC_IMPL_NO_DROP_CLONE_INNER(name, _ZP_NOTHING) #define _Z_OWNED_FUNCTIONS_SYSTEM_IMPL(type, name) \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE(name) \ void z_internal_##name##_null(z_owned_##name##_t *obj) { (void)obj; } \ const z_loaned_##name##_t *z_##name##_loan(const z_owned_##name##_t *obj) { return &obj->_val; } \ z_loaned_##name##_t *z_##name##_loan_mut(z_owned_##name##_t *obj) { return &obj->_val; } #define _Z_VIEW_FUNCTIONS_IMPL(type, name, f_check, f_null) \ bool z_view_##name##_is_empty(const z_view_##name##_t *obj) { return !f_check((&obj->_val)); } \ const z_loaned_##name##_t *z_view_##name##_loan(const z_view_##name##_t *obj) { return &obj->_val; } \ z_loaned_##name##_t *z_view_##name##_loan_mut(z_view_##name##_t *obj) { return &obj->_val; } \ void z_view_##name##_empty(z_view_##name##_t *obj) { obj->_val = f_null(); } #define _Z_OWNED_FUNCTIONS_CLOSURE_DEF_PREFIX(prefix, name) \ void prefix##_internal_##name##_null(prefix##_owned_##name##_t *name); \ bool prefix##_internal_##name##_check(const prefix##_owned_##name##_t *val); \ prefix##_moved_##name##_t *prefix##_##name##_move(prefix##_owned_##name##_t *val); \ void prefix##_##name##_take(prefix##_owned_##name##_t *obj, prefix##_moved_##name##_t *src); \ void prefix##_##name##_drop(prefix##_moved_##name##_t *obj); \ const prefix##_loaned_##name##_t *prefix##_##name##_loan(const prefix##_owned_##name##_t *val); #define _Z_OWNED_FUNCTIONS_CLOSURE_DEF(name) _Z_OWNED_FUNCTIONS_CLOSURE_DEF_PREFIX(z, name) // For value types #define _Z_OWNED_TYPE_VALUE_PREFIX(prefix, type, name) \ typedef struct { \ type _val; \ } prefix##_owned_##name##_t; \ _Z_LOANED_TYPE_PREFIX(prefix, type, name) \ _Z_MOVED_TYPE_PREFIX(prefix, name) #define _Z_OWNED_FUNCTIONS_CLOSURE_IMPL_PREFIX(prefix, name, f_call, f_drop) \ _Z_OWNED_FUNCTIONS_IMPL_MOVE_TAKE_PREFIX(prefix, name) \ void prefix##_internal_##name##_null(prefix##_owned_##name##_t *val) { \ val->_val.call = NULL; \ val->_val.drop = NULL; \ val->_val.context = NULL; \ } \ bool prefix##_internal_##name##_check(const prefix##_owned_##name##_t *val) { return val->_val.call != NULL; } \ void prefix##_##name##_drop(prefix##_moved_##name##_t *obj) { \ if (obj->_this._val.drop != NULL) { \ (obj->_this._val.drop)(obj->_this._val.context); \ obj->_this._val.drop = NULL; \ } \ obj->_this._val.call = NULL; \ obj->_this._val.context = NULL; \ } \ const prefix##_loaned_##name##_t *prefix##_##name##_loan(const prefix##_owned_##name##_t *val) { \ return &val->_val; \ } \ z_result_t prefix##_##name(prefix##_owned_##name##_t *closure, f_call call, f_drop drop, void *context) { \ closure->_val.call = call; \ closure->_val.drop = drop; \ closure->_val.context = context; \ \ return _Z_RES_OK; \ } #define _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(name, f_call, f_drop) \ _Z_OWNED_FUNCTIONS_CLOSURE_IMPL_PREFIX(z, name, f_call, f_drop) #endif /* INCLUDE_ZENOH_PICO_API_OLV_MACROS_H */ ================================================ FILE: include/zenoh-pico/api/primitives.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_PRIMITIVES_H #define INCLUDE_ZENOH_PICO_API_PRIMITIVES_H #ifndef SPHINX_DOCS // For some reason sphinx/clang doesn't handle bool types correctly if stdbool.h is included #include #endif #include #include "olv_macros.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/advanced_cache.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/net/subscribe.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif /** * Builds a :c:type:`z_view_string_t` by wrapping a ``const char *`` string. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_view_string_t`. * value: Pointer to a null terminated string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_string_from_str(z_view_string_t *str, const char *value); /** * Builds a :c:type:`z_view_string_t` by wrapping a ``const char *`` substring. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_view_string_t`. * value: Pointer to a null terminated string. * len: Size of the string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_string_from_substr(z_view_string_t *str, const char *value, size_t len); /** * Builds a :c:type:`z_keyexpr_t` from a null-terminated string. * It is a loaned key expression that aliases ``name``. * This function will fail if the string is not in canon form. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr as a null terminated string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_keyexpr_from_str(z_view_keyexpr_t *keyexpr, const char *name); /** * Builds a :c:type:`z_keyexpr_t` from a null-terminated string. * It is a loaned key expression that aliases ``name``. * Input key expression is not checked for correctness. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr as a null terminated string. */ void z_view_keyexpr_from_str_unchecked(z_view_keyexpr_t *keyexpr, const char *name); /** * Builds a :c:type:`z_view_keyexpr_t` from a null-terminated string with auto canonization. * It is a loaned key expression that aliases ``name``. * The string is canonized in-place before being passed to keyexpr, possibly shortening it by modifying len. * May SEGFAULT if `name` is NULL or lies in read-only memory (as values initialized with string literals do). * `name` must outlive the constructed key expression. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr as a null terminated string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_keyexpr_from_str_autocanonize(z_view_keyexpr_t *keyexpr, char *name); /** * Builds a :c:type:`z_keyexpr_t` by aliasing a substring. * It is a loaned key expression that aliases ``name``. * This function will fail if the string is not in canon form. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr. * len: Size of the string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_keyexpr_from_substr(z_view_keyexpr_t *keyexpr, const char *name, size_t len); /** * Builds a :c:type:`z_view_keyexpr_t` from a substring with auto canonization. * It is a loaned key expression that aliases ``name``. * The string is canonized in-place before being passed to keyexpr, possibly shortening it by modifying len. * May SEGFAULT if `name` is NULL or lies in read-only memory (as values initialized with string literals do). * `name` must outlive the constructed key expression. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr. * len: Pointer to the size of the string. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_view_keyexpr_from_substr_autocanonize(z_view_keyexpr_t *keyexpr, char *name, size_t *len); /** * Builds a :c:type:`z_keyexpr_t` from a substring. * It is a loaned key expression that aliases ``name``. * Input key expression is not checked for correctness. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_view_keyexpr_t`. * name: Pointer to string representation of the keyexpr. * len: Size of the string. */ void z_view_keyexpr_from_substr_unchecked(z_view_keyexpr_t *keyexpr, const char *name, size_t len); /** * Gets a string view from a :c:type:`z_keyexpr_t`. * * Parameters: * keyexpr: Pointer to a loaned instance of :c:type:`z_keyexpr_t`. * str: Pointer to an uninitialized :c:type:`z_view_string_t`. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_as_view_string(const z_loaned_keyexpr_t *keyexpr, z_view_string_t *str); /** * Constructs key expression by concatenation of key expression in `left` with a string in `right`. * * To avoid odd behaviors, concatenating a key expression starting with `*` to one ending with `*` is forbidden by this * operation, as this would extremely likely cause bugs. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to store the keyexpr. * left: Pointer to :c:type:`z_loaned_keyexpr_t` to keyexpr to concatenate to. * right: Pointer to the start of the substring that will be concatenated. * len: Length of the substring to concatenate. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_concat(z_owned_keyexpr_t *key, const z_loaned_keyexpr_t *left, const char *right, size_t len); /** * Constructs key expression by performing path-joining (automatically inserting '/'). The resulting key expression is * automatically canonized. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to store the keyexpr. * left: Pointer to :c:type:`z_loaned_keyexpr_t` to the left part of the resulting key expression. * right: Pointer to :c:type:`z_loaned_keyexpr_t` to the right part of the resulting key expression. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_join(z_owned_keyexpr_t *key, const z_loaned_keyexpr_t *left, const z_loaned_keyexpr_t *right); /** * Appends the suffix portion of a key expression to another key expression (automatically inserting '/'). * * Only the suffix portion of the key expression is preserved. All other components of the resulting key * expression will be discarded. * The resulting key expression is automatically canonized. * * Parameters: * prefix: Pointer to :c:type:`z_owned_keyexpr_t` to the key expression to append to. * right: Pointer to :c:type:`z_loaned_keyexpr_t` whose suffix will be appended. * * Return: * ``0`` if the append was successful; a ``negative value`` otherwise. */ z_result_t _z_keyexpr_append_suffix(z_owned_keyexpr_t *prefix, const z_loaned_keyexpr_t *right); /** * Appends a string segment to a key expression (automatically inserting '/'). The resulting key expression is * automatically canonized. * * Parameters: * prefix: Pointer to :c:type:`z_owned_keyexpr_t` to the key expression to append to. * right: Pointer to a character array representing the string to append. * len: Length of the string segment in ``right`` to append. * * Return: * ``0`` if append successful, ``negative value`` otherwise. */ z_result_t _z_keyexpr_append_substr(z_owned_keyexpr_t *prefix, const char *right, size_t len); /** * Appends a null-terminated string to a key expression (automatically inserting '/'). The resulting key expression is * automatically canonized. * * Parameters: * prefix: Pointer to :c:type:`z_owned_keyexpr_t` to the key expression to append to. * right: Pointer to a null-terminated string to append. * * Return: * ``0`` if append successful, ``negative value`` otherwise. */ static inline z_result_t _z_keyexpr_append_str(z_owned_keyexpr_t *prefix, const char *right) { // SAFETY: right is documented to be null-terminated. // Flawfinder: ignore [CWE-126] return _z_keyexpr_append_substr(prefix, right, right ? strlen(right) : 0); } /** * Appends multiple null-terminated strings to a key expression (automatically inserting '/' between each component). * The resulting key expression is automatically canonized. * * Parameters: * prefix: Pointer to :c:type:`z_owned_keyexpr_t` representing the key expression to append to. * strs: Array of ``count`` null-terminated strings to append, in order. * count: Number of strings in the array. * * Return: * ``0`` if all appends were successful, ``negative value`` if any append failed. */ z_result_t _z_keyexpr_append_str_array(z_owned_keyexpr_t *prefix, const char *strs[], size_t count); #define _Z_KEYEXPR_APPEND_STR_ARRAY(prefix, ...) \ _z_keyexpr_append_str_array(prefix, (const char *[]){__VA_ARGS__}, \ sizeof((const char *[]){__VA_ARGS__}) / sizeof(const char *)) /** * Returns the relation between `left` and `right` from the `left`'s point of view. * * Note that this is slower than `z_keyexpr_intersects` and `keyexpr_includes`, so you should favor these methods for * most applications. * * Parameters: * left: Pointer to :c:type:`z_loaned_keyexpr_t` representing left key expression. * right: Pointer to :c:type:`z_loaned_keyexpr_t` representing right key expression. * * Return: * Relation between `left` and `right` from the `left`'s point of view. */ z_keyexpr_intersection_level_t z_keyexpr_relation_to(const z_loaned_keyexpr_t *left, const z_loaned_keyexpr_t *right); /** * Checks if a given keyexpr is valid and in canonical form. * * Parameters: * start: Pointer to the keyexpr in its string representation as a non-null terminated string. * len: Number of characters in ``start``. * * Return: * ``0`` if the passed string is a valid (and canon) key expression, or a ``negative value`` otherwise. * Error codes are defined in :c:enum:`zp_keyexpr_canon_status_t`. */ z_result_t z_keyexpr_is_canon(const char *start, size_t len); /** * Canonizes of a given keyexpr in string representation. * The canonization is performed over the passed string, possibly shortening it by modifying ``len``. * * Parameters: * start: Pointer to the keyexpr in its string representation as a non-null terminated string. * len: Number of characters in ``start``. * * Return: * ``0`` if canonization successful, or a ``negative value`` otherwise. * Error codes are defined in :c:enum:`zp_keyexpr_canon_status_t`. */ z_result_t z_keyexpr_canonize(char *start, size_t *len); /** * Canonizes of a given keyexpr in string representation. * The canonization is performed over the passed string, possibly shortening it by setting null at the end. * * Parameters: * start: Pointer to the keyexpr in its string representation as a null terminated string. * * Return: * ``0`` if canonization successful, or a ``negative value`` otherwise. * Error codes are defined in :c:enum:`zp_keyexpr_canon_status_t`. */ z_result_t z_keyexpr_canonize_null_terminated(char *start); /** * Checks if a given keyexpr contains another keyexpr in its set. * * Parameters: * l: Pointer to a :c:type:`z_loaned_keyexpr_t`. * r: Pointer to a :c:type:`z_loaned_keyexpr_t`. * * Return: * ``true`` if ``l`` includes ``r``, i.e. the set defined by ``l`` contains every key belonging to the set * defined by ``r``. Otherwise, returns ``false``. */ bool z_keyexpr_includes(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r); /** * Checks if a given keyexpr intersects with another keyexpr. * * Parameters: * l: Pointer to a :c:type:`z_loaned_keyexpr_t`. * r: Pointer to a :c:type:`z_loaned_keyexpr_t`. * * Return: * ``true`` if keyexprs intersect, i.e. there exists at least one key which is contained in both of the * sets defined by ``l`` and ``r``. Otherwise, returns ``false``. */ bool z_keyexpr_intersects(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r); /** * Checks if two keyexpr are equal. * * Parameters: * l: Pointer to a :c:type:`z_loaned_keyexpr_t`. * r: Pointer to a :c:type:`z_loaned_keyexpr_t`. * * Return: * ``true`` if both ``l`` and ``r`` are equal. Otherwise, returns ``false``. */ bool z_keyexpr_equals(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r); /** * Builds a new, zenoh-allocated, default configuration. * It consists in a default set of properties for zenoh session configuration. * * Parameters: * config: Pointer to uninitialized :c:type:`z_owned_config_t`. * * Return: * ``0`` in case of success, or a ``negative value`` otherwise. */ z_result_t z_config_default(z_owned_config_t *config); /** * Gets the property with the given integer key from the configuration. * * Parameters: * config: Pointer to a :c:type:`z_loaned_config_t` to get the property from. * key: Integer key of the requested property. * * Return: * The requested property value. */ const char *zp_config_get(const z_loaned_config_t *config, uint8_t key); /** * Inserts or replaces the property with the given integer key in the configuration. * * Parameters: * config: Pointer to a :c:type:`z_loaned_config_t` to modify. * key: Integer key of the property to be inserted. * value: Property value to be inserted. * * Return: * ``0`` if insertion is successful, ``negative value`` otherwise. */ z_result_t zp_config_insert(z_loaned_config_t *config, uint8_t key, const char *value); /** * Builds a :c:type:`z_owned_encoding_t` from a null terminated string. * * Parameters: * encoding: Pointer to an uninitialized :c:type:`z_owned_encoding_t`. * s: Pointer to the null terminated string to use. * * Return: * ``0`` if creation is successful,``negative value`` otherwise. */ z_result_t z_encoding_from_str(z_owned_encoding_t *encoding, const char *s); /** * Builds a :c:type:`z_owned_encoding_t` from a null terminated string. * * Parameters: * encoding: Pointer to an uninitialized :c:type:`z_owned_encoding_t`. * s: Pointer to the string to use. * len: Number of characters from the string s to use. * * Return: * ``0`` if creation is successful,``negative value`` otherwise. */ z_result_t z_encoding_from_substr(z_owned_encoding_t *encoding, const char *s, size_t len); /** * Sets a schema to this encoding from a null-terminated string. Zenoh does not define what a schema is and its * semantics is left to the implementer. E.g. a common schema for `text/plain` encoding is `utf-8`. * * Parameters: * encoding: Pointer to initialized :c:type:`z_loaned_encoding_t`. * schema: Pointer to the null terminated string to use as a schema. * * Return: * ``0`` in case of success,``negative value`` otherwise. */ z_result_t z_encoding_set_schema_from_str(z_loaned_encoding_t *encoding, const char *schema); /** * Sets a schema to this encoding from a substring. Zenoh does not define what a schema is and its semantics is left * to the implementer. E.g. a common schema for `text/plain` encoding is `utf-8`. * * Parameters: * encoding: Pointer to initialized :c:type:`z_loaned_encoding_t`. * schema: Pointer to the substring start. * len: Number of characters to consider. * * Return: * ``0`` if in case of success,``negative value`` otherwise. */ z_result_t z_encoding_set_schema_from_substr(z_loaned_encoding_t *encoding, const char *schema, size_t len); /** * Builds a string from a :c:type:`z_loaned_encoding_t`. * * Parameters: * encoding: Pointer to the :c:type:`z_loaned_encoding_t` to use. * string: Pointer to an uninitialized :c:type:`z_owned_string_t` to store the string. * * Return: * ``0`` if creation is successful,``negative value`` otherwise. */ z_result_t z_encoding_to_string(const z_loaned_encoding_t *encoding, z_owned_string_t *string); /** * Checks if two encodings are equal. * * Parameters: * left: Pointer to the first :c:type:`z_loaned_encoding_t` to compare. * right: Pointer to the second :c:type:`z_loaned_encoding_t` to compare. * * Return: * ``true`` if `left` equals `right`, ``false`` otherwise. */ bool z_encoding_equals(const z_loaned_encoding_t *left, const z_loaned_encoding_t *right); /** * Gets the bytes data from a reply error payload by aliasing it. * * Parameters: * reply_err: Pointer to a :c:type:`z_loaned_reply_err_t` to get data from. * * Return: * Pointer to the data as a :c:type:`z_loaned_bytes_t`. */ const z_loaned_bytes_t *z_reply_err_payload(const z_loaned_reply_err_t *reply_err); /** * Gets a reply error encoding by aliasing it. * * Parameters: * reply_err: Pointer to the :c:type:`z_loaned_reply_err_t` to get the encoding from. * * Return: * Pointer to the encoding as a :c:type:`z_loaned_encoding_t`. */ const z_loaned_encoding_t *z_reply_err_encoding(const z_loaned_reply_err_t *reply_err); /** * Builds a :c:type:`z_owned_slice_t` by copying a buffer into it. * * Parameters: * slice: Pointer to an uninitialized :c:type:`z_owned_slice_t`. * data: Pointer to the data that will be copied into slice. * len: Number of bytes to copy. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_slice_copy_from_buf(z_owned_slice_t *slice, const uint8_t *data, size_t len); /** * Builds a :c:type:`z_owned_slice_t` by transferring ownership over a data to it. * * Parameters: * slice: Pointer to an uninitialized :c:type:`z_owned_slice_t`. * data: Pointer to the data to be owned by `slice`. * len: Number of bytes in `data`. * deleter: A thread-safe delete function to free the `data`. Will be called once when `slice` is dropped. * Can be NULL in the case where `data` is allocated in static memory. * context: An optional context to be passed to the `deleter`. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_slice_from_buf(z_owned_slice_t *slice, uint8_t *data, size_t len, void (*deleter)(void *data, void *context), void *context); /** * Builds a :c:type:`z_view_slice_t`. * * Parameters: * slice: Pointer to an uninitialized :c:type:`z_view_slice_t`. * data: Pointer to the data to be pointed by `slice`. * len: Number of bytes in `data`. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_view_slice_from_buf(z_view_slice_t *slice, const uint8_t *data, size_t len); /** * Builds an empty :c:type:`z_owned_slice_t`. * * Parameters: * slice: Pointer to an uninitialized :c:type:`z_owned_slice_t`. */ void z_slice_empty(z_owned_slice_t *slice); /** * Gets date pointer of a bytes array. * * Parameters: * slice: Pointer to a :c:type:`z_loaned_slice_t` to get data from. * * Return: * The data pointer. */ const uint8_t *z_slice_data(const z_loaned_slice_t *slice); /** * Gets the total number of bytes in a bytes array. * * Parameters: * slice: Pointer to a :c:type:`z_loaned_slice_t` to get length from. * * Return: * The number of bytes. */ size_t z_slice_len(const z_loaned_slice_t *slice); /** * Checks if slice is empty * * Parameters: * slice: Pointer to a :c:type:`z_loaned_slice_t` to check. * * Return: * ``true`` if the container is empty, ``false`` otherwise. */ bool z_slice_is_empty(const z_loaned_slice_t *slice); /** * Converts data into a :c:type:`z_owned_slice_t` * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to decode. * dst: Pointer to an uninitialized :c:type:`z_owned_slice_t` to contain the decoded slice. * * Return: * ``0`` if decode is successful, or a ``negative value`` otherwise. */ z_result_t z_bytes_to_slice(const z_loaned_bytes_t *bytes, z_owned_slice_t *dst); /** * Converts data into a :c:type:`z_owned_string_t` * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to decode. * str: Pointer to an uninitialized :c:type:`z_owned_string_t` to contain the decoded string. * * Return: * ``0`` if decode is successful, or a ``negative value`` otherwise. */ z_result_t z_bytes_to_string(const z_loaned_bytes_t *bytes, z_owned_string_t *str); /** * Converts a slice into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded slice. * slice: Pointer to the slice to convert. The slice will be consumed upon function return. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_slice(z_owned_bytes_t *bytes, z_moved_slice_t *slice); /** * Converts a slice into a :c:type:`z_owned_bytes_t` by copying. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded slice. * slice: Pointer to the slice to convert. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_copy_from_slice(z_owned_bytes_t *bytes, const z_loaned_slice_t *slice); /** * Converts data into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded data. * data: Pointer to the data to convert. Ownership is transferred to the `bytes`. * len: Number of bytes to consider. * deleter: A thread-safe delete function to free the `data`. Will be called once when `bytes` is dropped. * Can be NULL in the case where `data` is allocated in static memory. * context: An optional context to be passed to the `deleter`. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_buf(z_owned_bytes_t *bytes, uint8_t *data, size_t len, void (*deleter)(void *data, void *context), void *context); /** * Converts data into a :c:type:`z_owned_bytes_t` by copying. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded data. * data: Pointer to the data to convert. * len: Number of bytes to consider. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_copy_from_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len); /** * Converts statically allocated constant data into a :c:type:`z_owned_bytes_t` by aliasing. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded data. * data: Pointer to the statically allocated constant data to encode. * len: Number of bytes to consider. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_static_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len); /** * Converts a string into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded string. * s: Pointer to the string to convert. The string will be consumed upon function return. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_string(z_owned_bytes_t *bytes, z_moved_string_t *s); /** * Converts a string into a :c:type:`z_owned_bytes_t` by copying. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded string. * s: Pointer to the string to convert. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_copy_from_string(z_owned_bytes_t *bytes, const z_loaned_string_t *s); /** * Converts a null-terminated string into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded string. * value: Pointer to the string to converts. Ownership is transferred to the `bytes`. * deleter: A thread-safe delete function to free the `value`. Will be called once when `bytes` is dropped. * Can be NULL in the case where `value` is allocated in static memory. * context: An optional context to be passed to the `deleter`. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_str(z_owned_bytes_t *bytes, char *value, void (*deleter)(void *value, void *context), void *context); /** * Converts a null-terminated string into a :c:type:`z_owned_bytes_t` by copying. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded string. * value: Pointer to the string to converts. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_copy_from_str(z_owned_bytes_t *bytes, const char *value); /** * Converts a statically allocated constant null-terminated string into a :c:type:`z_owned_bytes_t` by aliasing. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the encoded string. * value: Pointer to the statically allocated constant string to convert. * * Return: * ``0`` if conversion is successful, ``negative value`` otherwise. */ z_result_t z_bytes_from_static_str(z_owned_bytes_t *bytes, const char *value); /** * Constructs an empty payload. * * Parameters: * bytes: Pointer to an unitialized :c:type:`z_loaned_bytes_t` instance. */ void z_bytes_empty(z_owned_bytes_t *bytes); /** * Returns total number of bytes in the container. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to decode. * * Return: * Number of the bytes in the container. */ size_t z_bytes_len(const z_loaned_bytes_t *bytes); /** * Checks if container is empty * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to decode. * * Return: * ``true`` if conainer is empty, ``false`` otherwise. */ bool z_bytes_is_empty(const z_loaned_bytes_t *bytes); #if defined(Z_FEATURE_UNSTABLE_API) /** * Attempts to get a contiguous view to the underlying bytes (unstable). * * This is only possible if data is not fragmented, otherwise the function will fail. * In case of fragmented data, consider using `z_bytes_get_slice_iterator()`. * * Parameters: * bytes: An instance of Zenoh data. * view: An uninitialized memory location where a contiguous view on data will be constructed. * * Return: * ``0`` in case of success, ``negative value`` otherwise. */ z_result_t z_bytes_get_contiguous_view(const z_loaned_bytes_t *bytes, z_view_slice_t *view); #endif /** * Returns an iterator on raw bytes slices contained in the `z_loaned_bytes_t`. * * Zenoh may store data in non-contiguous regions of memory, this iterator * then allows to access raw data directly without any attempt of deserializing it. * Please note that no guarantee is provided on the internal memory layout. * The only provided guarantee is on the bytes order that is preserved. * * Parameters: * bytes: Data to iterate over. * * Return: * The constructed :c:type:`z_bytes_slice_iterator_t`. */ z_bytes_slice_iterator_t z_bytes_get_slice_iterator(const z_loaned_bytes_t *bytes); /** * Constructs :c:type:`z_view_slice_t` providing view to the next slice. * * Parameters: * iter: An iterator over slices of serialized data. * out: An uninitialized :c:type:`z_view_slice_t` that will contain next slice. * * Return: * ``false`` when iterator reaches the end, ``true`` otherwise. */ bool z_bytes_slice_iterator_next(z_bytes_slice_iterator_t *iter, z_view_slice_t *out); /** * Returns a reader for the `bytes`. * * The `bytes` should outlive the reader and should not be modified, while reader is in use. * * Parameters: * bytes: Data to read. * * Return: * The constructed :c:type:`z_bytes_reader_t`. */ z_bytes_reader_t z_bytes_get_reader(const z_loaned_bytes_t *bytes); /** * Reads data into specified destination. * * Parameters: * reader: Data reader to read from. * dst: Buffer where the read data is written. * len: Maximum number of bytes to read. * * Return: * Number of bytes read. If return value is smaller than `len`, it means that the end of the data was reached. */ size_t z_bytes_reader_read(z_bytes_reader_t *reader, uint8_t *dst, size_t len); /** * Sets the `reader` position indicator for the payload to the value pointed to by offset. * The new position is exactly `offset` bytes measured from the beginning of the payload if origin is `SEEK_SET`, * from the current reader position if origin is `SEEK_CUR`, and from the end of the payload if origin is `SEEK_END`. * * Parameters: * reader: Data reader to reposition. * offset: New position ffset in bytes. * origin: Origin for the new position. * * Return: * ``0`` in case of success, ``negative value`` otherwise. */ z_result_t z_bytes_reader_seek(z_bytes_reader_t *reader, int64_t offset, int origin); /** * Gets the read position indicator. * * Parameters: * reader: Data reader to get position of. * * Return: * Read position indicator on success or -1L if failure occurs. */ int64_t z_bytes_reader_tell(z_bytes_reader_t *reader); /** * Gets number of bytes that can still be read. * * Parameters: * reader: Data reader. * * Return: * Number of bytes that can still be read. */ size_t z_bytes_reader_remaining(const z_bytes_reader_t *reader); /** * Constructs an empty writer for payload. * * Parameters: * writer: An uninitialized memory location where writer is to be constructed. * * Return: * ``0`` in case of success, ``negative value`` otherwise. */ z_result_t z_bytes_writer_empty(z_owned_bytes_writer_t *writer); /** * Finishes writing and returns underlying bytes. * * Parameters: * writer: A data writer. * bytes: An uninitialized memory location where bytes is to be constructed. */ void z_bytes_writer_finish(z_moved_bytes_writer_t *writer, z_owned_bytes_t *bytes); /** * Writes `len` bytes from `src` into underlying :c:type:`z_loaned_bytes_t`. * * Parameters: * writer: A data writer. * src: Buffer to write from. * len: Number of bytes to write. * * Return: * ``0`` if write is successful, ``negative value`` otherwise. */ z_result_t z_bytes_writer_write_all(z_loaned_bytes_writer_t *writer, const uint8_t *src, size_t len); /** * Appends bytes. * This allows to compose a serialized data out of multiple `z_owned_bytes_t` that may point to different memory * regions. Said in other terms, it allows to create a linear view on different memory regions without copy. * * Parameters: * writer: A data writer. * bytes: A data to append. * * Return: * ``0`` if write is successful, ``negative value`` otherwise. */ z_result_t z_bytes_writer_append(z_loaned_bytes_writer_t *writer, z_moved_bytes_t *bytes); /** * Create timestamp. * * Parameters: * ts: An uninitialized :c:type:`z_timestamp_t`. * zs: Pointer to a :c:type:`z_loaned_session_t` to get the id from. * * Return: * ``0`` if encode is successful, ``negative value`` otherwise (for example if RTC is not available on the system). */ z_result_t z_timestamp_new(z_timestamp_t *ts, const z_loaned_session_t *zs); /** * Returns NTP64 time associated with this timestamp. * * Parameters: * ts: Pointer to the valid :c:type:`z_timestamp_t`. * * Return: * NTP64 time value */ uint64_t z_timestamp_ntp64_time(const z_timestamp_t *ts); /** * Returns id associated with this timestamp. * * Parameters: * ts: Pointer to the valid :c:type:`z_timestamp_t`. * * Return: * Associated id represented by c:type:`z_id_t` */ z_id_t z_timestamp_id(const z_timestamp_t *ts); /** * Creates an entity global id. * * Parameters: * gid: An uninitialized :c:type:`z_entity_global_id_t`. * zid: Pointer to a :c:type:`z_id_t` zenoh id. * eid: :c:type:`uint32_t` entity id. */ z_result_t z_entity_global_id_new(z_entity_global_id_t *gid, const z_id_t *zid, uint32_t eid); /** * Returns the entity id of the entity global id. * * Parameters: * gid: Pointer to the valid :c:type:`z_entity_global_id_t`. * * Return: * Entity id represented by c:type:`uint32_t`. */ uint32_t z_entity_global_id_eid(const z_entity_global_id_t *gid); /** * Returns the zenoh id of entity global id. * * Parameters: * gid: Pointer to the valid :c:type:`z_entity_global_id_t`. * * Return: * Zenoh id represented by c:type:`z_id_t`. */ z_id_t z_entity_global_id_zid(const z_entity_global_id_t *gid); /** * Constructs a new source info. * * Parameters: * source_id: Non-null pointer to a :c:type:`z_entity_global_id_t` global entity id. * source_sn: :c:type:`uint32_t` sequence number. * * Return: * Source info. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_source_info_t z_source_info_new(const z_entity_global_id_t *source_id, uint32_t source_sn); /** * Returns the sequence number associated with this source info. * * Parameters: * info: Pointer to the :c:type:`z_source_info_t` to get the sequence number from. * * Return: * :c:type:`uint32_t` sequence number. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ uint32_t z_source_info_sn(const z_source_info_t *info); /** * Returns the id associated with this source info. * * Parameters: * info: Pointer to the :c:type:`z_source_info_t` to get the id from. * * Return: * Global entity ID as a :c:type:`z_entity_global_id_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_entity_global_id_t z_source_info_id(const z_source_info_t *info); /** * Builds a default query target. * * Return: * The constructed :c:type:`z_query_target_t`. */ z_query_target_t z_query_target_default(void); /** * Builds a default query reply key expression type. * * Return: * The constructed :c:type:`z_reply_keyexpr_t`. */ z_reply_keyexpr_t z_reply_keyexpr_default(void); /** * Builds an automatic query consolidation :c:type:`z_query_consolidation_t`. * * A query consolidation strategy will automatically be selected depending on the query selector. * If selector contains time range properties, no consolidation is performed. * Otherwise the :c:func:`z_query_consolidation_latest` strategy is used. * * Return: * The constructed :c:type:`z_query_consolidation_t`. */ z_query_consolidation_t z_query_consolidation_auto(void); /** * Builds a default :c:type:`z_query_consolidation_t`. * * Return: * The constructed :c:type:`z_query_consolidation_t`. */ z_query_consolidation_t z_query_consolidation_default(void); /** * Builds a latest query consolidation :c:type:`z_query_consolidation_t`. * * This strategy optimizes bandwidth on all links in the system but will provide a very poor latency. * * Return: * The constructed :c:type:`z_query_consolidation_t`. */ z_query_consolidation_t z_query_consolidation_latest(void); /** * Builds a monotonic query consolidation :c:type:`z_query_consolidation_t`. * * This strategy offers the best latency. Replies are directly transmitted to the application when received * without needing to wait for all replies. This mode does not guarantee that there will be no duplicates. * * Return: * The constructed :c:type:`z_query_consolidation_t`. */ z_query_consolidation_t z_query_consolidation_monotonic(void); /** * Builds a no query consolidation :c:type:`z_query_consolidation_t`. * * This strategy is useful when querying timeseries data bases or when using quorums. * * Return: * The constructed :c:type:`z_query_consolidation_t`. */ z_query_consolidation_t z_query_consolidation_none(void); /** * Gets a query parameters field. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the parameters from. * parameters: Pointer to an uninitialized :c:type:`z_view_string_t` to contain the parameters. */ void z_query_parameters(const z_loaned_query_t *query, z_view_string_t *parameters); /** * Queries may or may not accept replies on key expressions that do not intersect with their own key expression. * This getter allows you to check whether or not a specific query does so. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the target from. * * Return: * The query reply key expression type as a :c:type:`z_reply_keyexpr_t`. */ z_reply_keyexpr_t z_query_accepts_replies(const z_loaned_query_t *query); /** * Gets a query payload by aliasing it. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the value from. * * Return: * Pointer to the payload as a :c:type:`z_loaned_bytes_t`. */ const z_loaned_bytes_t *z_query_payload(const z_loaned_query_t *query); /** * Gets a query encoding by aliasing it. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the value from. * * Return: * Pointer to the encoding as a :c:type:`z_loaned_encoding_t`. */ const z_loaned_encoding_t *z_query_encoding(const z_loaned_query_t *query); /** * Gets a query attachment value by aliasing it. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the attachment from. * * Return: * Pointer to the attachment as a :c:type:`z_loaned_bytes_t`. */ const z_loaned_bytes_t *z_query_attachment(const z_loaned_query_t *query); /** * Gets a query keyexpr by aliasing it. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a:c:type:`z_keyexpr_t`. */ const z_loaned_keyexpr_t *z_query_keyexpr(const z_loaned_query_t *query); /** * Gets a query source info by aliasing it. * * Parameters: * query: Pointer to the :c:type:`z_loaned_query_t` to get the value from. * * Return: * Pointer to the source info as a :c:type:`z_source_info_t`. Will return NULL if source info was not set by querier. */ const z_source_info_t *z_query_source_info(const z_loaned_query_t *query); /** * Builds a new sample closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_sample_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_sample(z_owned_closure_sample_t *closure, z_closure_sample_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a sample closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_sample_t` to call. * sample: Pointer to the :c:type:`z_loaned_sample_t` to pass to the closure. */ void z_closure_sample_call(const z_loaned_closure_sample_t *closure, z_loaned_sample_t *sample); /** * Builds a new query closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_query_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_query(z_owned_closure_query_t *closure, z_closure_query_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a query closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_query_t` to call. * query: Pointer to the :c:type:`z_loaned_query_t` to pass to the closure. */ void z_closure_query_call(const z_loaned_closure_query_t *closure, z_loaned_query_t *query); /** * Builds a new reply closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_reply_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_reply(z_owned_closure_reply_t *closure, z_closure_reply_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a reply closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_reply_t` to call. * reply: Pointer to the :c:type:`z_loaned_reply_t` to pass to the closure. */ void z_closure_reply_call(const z_loaned_closure_reply_t *closure, z_loaned_reply_t *reply); /** * Builds a new hello closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_hello_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_hello(z_owned_closure_hello_t *closure, z_closure_hello_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a hello closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_hello_t` to call. * hello: Pointer to the :c:type:`z_loaned_hello_t` to pass to the closure. */ void z_closure_hello_call(const z_loaned_closure_hello_t *closure, z_loaned_hello_t *hello); /** * Builds a new zid closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_zid_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_zid(z_owned_closure_zid_t *closure, z_closure_zid_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a zid closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_zid_t` to call. * zid: Pointer to the :c:type:`z_id_t` to pass to the closure. */ void z_closure_zid_call(const z_loaned_closure_zid_t *closure, const z_id_t *id); #if Z_FEATURE_CONNECTIVITY == 1 /** * Builds a new transport closure. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_transport_t`. * call: Pointer to the callback function. * drop: Pointer to callback-state cleanup function. * context: Pointer to arbitrary callback state. * * Return: * ``0`` in case of success, negative error code otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_closure_transport(z_owned_closure_transport_t *closure, z_closure_transport_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a transport closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_closure_transport_call(const z_loaned_closure_transport_t *closure, z_loaned_transport_t *transport); /** * Builds a new link closure. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_link_t`. * call: Pointer to the callback function. * drop: Pointer to callback-state cleanup function. * context: Pointer to arbitrary callback state. * * Return: * ``0`` in case of success, negative error code otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_closure_link(z_owned_closure_link_t *closure, z_closure_link_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a link closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_closure_link_call(const z_loaned_closure_link_t *closure, z_loaned_link_t *link); /** * Builds a new transport event closure. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_transport_event_t`. * call: Pointer to the callback function. * drop: Pointer to callback-state cleanup function. * context: Pointer to arbitrary callback state. * * Return: * ``0`` in case of success, negative error code otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_closure_transport_event(z_owned_closure_transport_event_t *closure, z_closure_transport_event_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a transport event closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_closure_transport_event_call(const z_loaned_closure_transport_event_t *closure, z_loaned_transport_event_t *event); /** * Builds a new link event closure. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_link_event_t`. * call: Pointer to the callback function. * drop: Pointer to callback-state cleanup function. * context: Pointer to arbitrary callback state. * * Return: * ``0`` in case of success, negative error code otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_closure_link_event(z_owned_closure_link_event_t *closure, z_closure_link_event_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a link event closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_closure_link_event_call(const z_loaned_closure_link_event_t *closure, z_loaned_link_event_t *event); #endif /** * Builds a new matching status closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`z_owned_closure_matching_status_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise */ z_result_t z_closure_matching_status(z_owned_closure_matching_status_t *closure, z_closure_matching_status_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a matching status closure. * * Parameters: * closure: Pointer to the :c:type:`z_loaned_closure_matching_status_t` to call. * status: Pointer to the :c:type:`z_matching_status_t` to pass to the closure. */ void z_closure_matching_status_call(const z_loaned_closure_matching_status_t *closure, const z_matching_status_t *status); /** * Builds a new sample miss closure. * It consists of a structure that contains all the elements for stateful, memory-leak-free callbacks. * * Parameters: * closure: Pointer to an uninitialized :c:type:`ze_owned_closure_miss_t`. * call: Pointer to the callback function. ``context`` will be passed as its last argument. * drop: Pointer to the function that will free the callback state. ``context`` will be passed as its last argument. * context: Pointer to an arbitrary state. * * Return: * ``0`` in case of success, negative error code otherwise * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t ze_closure_miss(ze_owned_closure_miss_t *closure, ze_closure_miss_callback_t call, z_closure_drop_callback_t drop, void *context); /** * Calls a sample miss closure. * * Parameters: * closure: Pointer to the :c:type:`ze_loaned_closure_miss_t` to call. * status: Pointer to the :c:type:`ze_miss_t` to pass to the closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ void ze_closure_miss_call(const ze_loaned_closure_miss_t *closure, const ze_miss_t *miss); /**************** Loans ****************/ _Z_OWNED_FUNCTIONS_DEF(string) _Z_OWNED_FUNCTIONS_DEF(keyexpr) _Z_OWNED_FUNCTIONS_DEF(config) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(session) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(subscriber) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(publisher) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(querier) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(matching_listener) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(queryable) _Z_OWNED_FUNCTIONS_DEF(hello) _Z_OWNED_FUNCTIONS_DEF(reply) _Z_OWNED_FUNCTIONS_DEF(string_array) _Z_OWNED_FUNCTIONS_DEF(sample) _Z_OWNED_FUNCTIONS_DEF(query) _Z_OWNED_FUNCTIONS_DEF(slice) _Z_OWNED_FUNCTIONS_DEF(bytes) _Z_OWNED_FUNCTIONS_NO_COPY_DEF(bytes_writer) _Z_OWNED_FUNCTIONS_DEF(reply_err) _Z_OWNED_FUNCTIONS_DEF(encoding) _Z_OWNED_FUNCTIONS_DEF(cancellation_token) #if Z_FEATURE_CONNECTIVITY == 1 _Z_OWNED_FUNCTIONS_DEF(transport) _Z_OWNED_FUNCTIONS_DEF(link) _Z_OWNED_FUNCTIONS_DEF(transport_event) _Z_OWNED_FUNCTIONS_DEF(link_event) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(transport_events_listener) _Z_OWNED_FUNCTIONS_NO_COPY_NO_MOVE_DEF(link_events_listener) #endif _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_sample) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_query) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_reply) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_hello) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_zid) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_matching_status) _Z_OWNED_FUNCTIONS_CLOSURE_DEF_PREFIX(ze, closure_miss) #if Z_FEATURE_CONNECTIVITY == 1 _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_transport) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_link) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_transport_event) _Z_OWNED_FUNCTIONS_CLOSURE_DEF(closure_link_event) #endif _Z_VIEW_FUNCTIONS_DEF(keyexpr) _Z_VIEW_FUNCTIONS_DEF(string) _Z_VIEW_FUNCTIONS_DEF(slice) /** * Loans a :c:type:`z_owned_sample_t`. * * Parameters: * sample: Pointer to a :c:type:`z_owned_sample_t` to loan. * * Return: * Pointer to the loaned sample as a :c:type:`z_loaned_sample_t`. */ const z_loaned_sample_t *z_sample_loan(const z_owned_sample_t *sample); /** * Gets data from a :c:type:`z_loaned_string_t`. * * Parameters: * str: Pointer to a :c:type:`z_loaned_string_t` to get data from. * * Return: * Pointer to the string data. */ const char *z_string_data(const z_loaned_string_t *str); /** * Gets string length from a :c:type:`z_loaned_string_t`. * * Parameters: * str: Pointer to a :c:type:`z_loaned_string_t` to get length from. * * Return: * Length of the string. */ size_t z_string_len(const z_loaned_string_t *str); /** * Builds a :c:type:`z_string_t` by copying a ``const char *`` string. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_owned_string_t`. * value: Pointer to a null terminated string to be copied. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_string_copy_from_str(z_owned_string_t *str, const char *value); /** * Builds a :c:type:`z_owned_string_t` by transferring ownership over a null-terminated string to it. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_owned_string_t`. * value: Pointer to a null terminated string to be owned by `str`. * deleter: A thread-safe delete function to free the `value`. Will be called once when `str` is dropped. * Can be NULL in the case where `value` is allocated in static memory. * context: An optional context to be passed to the `deleter`. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_string_from_str(z_owned_string_t *str, char *value, void (*deleter)(void *value, void *context), void *context); /** * Builds an empty :c:type:`z_owned_string_t`. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_owned_string_t`. */ void z_string_empty(z_owned_string_t *str); /** * Builds a :c:type:`z_string_t` by wrapping a substring specified by ``const char *`` and length `len`. * * Parameters: * str: Pointer to an uninitialized :c:type:`z_owned_string_t`. * value: Pointer to a string. * len: String size. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_string_copy_from_substr(z_owned_string_t *str, const char *value, size_t len); /** * Checks if string is empty * * Parameters: * str: Pointer to a :c:type:`z_loaned_string_t` to check. * * Return: * ``true`` if the string is empty, ``false`` otherwise. */ bool z_string_is_empty(const z_loaned_string_t *str); /** * Returns :c:type:`z_loaned_slice_t` for the string * * Parameters: * str: Pointer to a :c:type:`z_loaned_string_t` to get a slice. * * Return: * slice containing string data */ const z_loaned_slice_t *z_string_as_slice(const z_loaned_string_t *str); /** * Returns default :c:type:`z_priority_t` value */ z_priority_t z_priority_default(void); #if Z_FEATURE_SCOUTING == 1 /** * Returns id of Zenoh entity that transmitted hello message. * * Parameters: * hello: Pointer to a :c:type:`z_loaned_hello_t` message. * * Return: * Id of the Zenoh entity that transmitted hello message. */ z_id_t z_hello_zid(const z_loaned_hello_t *hello); /** * Returns type of Zenoh entity that transmitted hello message. * * Parameters: * hello: Pointer to a :c:type:`z_loaned_hello_t` message. * * Return: * Type of the Zenoh entity that transmitted hello message. */ z_whatami_t z_hello_whatami(const z_loaned_hello_t *hello); /** * Returns an array of locators of Zenoh entity that sent hello message. * * Parameters: * hello: Pointer to a :c:type:`z_loaned_hello_t` message. * * Return: * :c:type:`z_loaned_string_array_t` containing locators. */ const z_loaned_string_array_t *zp_hello_locators(const z_loaned_hello_t *hello); /** * Constructs an array of locators of Zenoh entity that sent hello message. * * Note that it is a method for zenoh-c compatiblity, in zenoh-pico :c:func:`zp_hello_locators` * can be used. * * Parameters: * hello: Pointer to a :c:type:`z_loaned_hello_t` message. * locators_out: An uninitialized memory location where :c:type:`z_owned_string_array_t` will be constructed. */ void z_hello_locators(const z_loaned_hello_t *hello, z_owned_string_array_t *locators_out); /** * Constructs a non-owned non-null-terminated string from the kind of zenoh entity. * * The string has static storage (i.e. valid until the end of the program). * * Parameters: * whatami: A whatami bitmask of zenoh entity kind. * str_out: An uninitialized memory location where strring will be constructed. * * Return: * ``0`` in case of success, ``negative value`` otherwise. */ z_result_t z_whatami_to_view_string(z_whatami_t whatami, z_view_string_t *str_out); /************* Primitives **************/ /** * Scouts for other Zenoh entities like routers and/or peers. * * Parameters: * config: Moved :c:type:`z_owned_config_t` to configure the scouting with. * callback: Moved :c:type:`z_owned_closure_hello_t` callback. * options: Pointer to a :c:type:`z_scout_options_t` to configure the operation. * * Return: * ``0`` if scouting was successfully triggered, ``negative value`` otherwise. */ z_result_t z_scout(z_moved_config_t *config, z_moved_closure_hello_t *callback, const z_scout_options_t *options); /** * Builds a :c:type:`z_scout_options_t` with default value. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_scout_options_t`. */ void z_scout_options_default(z_scout_options_t *options); #endif /** * Opens a Zenoh session. * * See :doc:`/config` for the configuration options that affect this operation. * * Parameters: * zs: Pointer to an uninitialized :c:type:`z_owned_session_t` to store the session info. * config: Moved :c:type:`z_owned_config_t` to configure the session with. * options: Pointer to a :c:type:`z_open_options_t` to configure the operation. * * Return: * ``0`` if open is successful, ``negative value`` otherwise. */ z_result_t z_open(z_owned_session_t *zs, z_moved_config_t *config, const z_open_options_t *options); /** * Builds a :c:type:`z_open_options_t` with default value. */ void z_open_options_default(z_open_options_t *options); /** * Closes a Zenoh session. * * Parameters: * zs: Loaned :c:type:`z_owned_session_t` to close. * options: Pointer to a :c:type:`z_close_options_t` to configure the operation. * * Return: * ``0`` if close is successful, ``negative value`` otherwise. */ z_result_t z_close(z_loaned_session_t *zs, const z_close_options_t *options); /** * Checks if Zenoh session is closed. * * Parameters: * zs: Loaned :c:type:`z_owned_session_t`. * * Return: * ``true`` if session is closed, ``false`` otherwise. */ bool z_session_is_closed(const z_loaned_session_t *zs); #ifdef Z_FEATURE_UNSTABLE_API /** * Gets the entity global Id of Zenoh session (unstable). * * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to get the id from. * * Return: * The entity global Id of the session as :c:type:`z_entity_t`. */ z_entity_global_id_t z_session_id(const z_loaned_session_t *zs); #endif /** * Fetches Zenoh IDs of all connected peers. * * The callback will be called once for each ID. It is guaranteed to never be called concurrently, * and to be dropped before this function exits. * * Parameters: * zs: Pointer to :c:type:`z_loaned_session_t` to fetch peer id from. * callback: Moved :c:type:`z_owned_closure_zid_t` callback. * * Return: * ``0`` if operation was successfully triggered, ``negative value`` otherwise. */ z_result_t z_info_peers_zid(const z_loaned_session_t *zs, z_moved_closure_zid_t *callback); /** * Fetches Zenoh IDs of all connected routers. * * The callback will be called once for each ID. It is guaranteed to never be called concurrently, * and to be dropped before this function exits. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to fetch router id from. * callback: Moved :c:type:`z_owned_closure_zid_t` callback. * * Return: * ``0`` if operation was successfully triggered, ``negative value`` otherwise. */ z_result_t z_info_routers_zid(const z_loaned_session_t *zs, z_moved_closure_zid_t *callback); /** * Gets the local Zenoh ID associated to a given Zenoh session. * * If this function returns an array of 16 zeros, this means the session is invalid. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to get the id from. * * Return: * The local Zenoh ID of the session as :c:type:`z_id_t`. */ z_id_t z_info_zid(const z_loaned_session_t *zs); static inline void _z_transport_link_properties_from_transport(const _z_transport_common_t *transport, uint16_t *mtu, bool *is_streamed, bool *is_reliable) { *mtu = 0; *is_streamed = false; *is_reliable = false; if (transport != NULL && transport->_link != NULL) { *mtu = transport->_link->_mtu; *is_streamed = transport->_link->_cap._flow == Z_LINK_CAP_FLOW_STREAM; *is_reliable = transport->_link->_cap._is_reliable; } } #if Z_FEATURE_CONNECTIVITY == 1 void _z_info_transport_from_peer(_z_info_transport_t *out, const _z_transport_peer_common_t *peer, bool is_multicast); bool _z_info_transport_filter_match(const _z_info_transport_t *transport, const _z_info_transport_t *filter); /** * Fetches all currently connected transports. * * The callback is called once for each transport and is dropped before this function exits. * * Parameters: * zs: Pointer to :c:type:`z_loaned_session_t`. * callback: Moved :c:type:`z_owned_closure_transport_t` callback. * * Return: * ``0`` if operation was successfully triggered, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_info_transports(const z_loaned_session_t *zs, z_moved_closure_transport_t *callback); /** * Constructs default value for :c:type:`z_info_links_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_info_links_options_default(z_info_links_options_t *options); /** * Fetches all currently connected links. * * The callback is called once for each link and is dropped before this function exits. * * Parameters: * zs: Pointer to :c:type:`z_loaned_session_t`. * callback: Moved :c:type:`z_owned_closure_link_t` callback. * options: Optional :c:type:`z_info_links_options_t`. * * Return: * ``0`` if operation was successfully triggered, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_info_links(const z_loaned_session_t *zs, z_moved_closure_link_t *callback, z_info_links_options_t *options); /** * Constructs default value for :c:type:`z_transport_events_listener_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_transport_events_listener_options_default(z_transport_events_listener_options_t *options); /** * Declares a transport events listener. * * Parameters: * zs: Pointer to :c:type:`z_loaned_session_t`. * listener: Uninitialized location where listener will be constructed. * callback: Moved :c:type:`z_owned_closure_transport_event_t`. * options: Optional :c:type:`z_transport_events_listener_options_t`. * * Return: * ``0`` on success, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_declare_transport_events_listener(const z_loaned_session_t *zs, z_owned_transport_events_listener_t *listener, z_moved_closure_transport_event_t *callback, const z_transport_events_listener_options_t *options); /** * Declares a background transport events listener. * * The listener runs in background and cannot be undeclared explicitly. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_declare_background_transport_events_listener(const z_loaned_session_t *zs, z_moved_closure_transport_event_t *callback, const z_transport_events_listener_options_t *options); /** * Undeclares a transport events listener. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_undeclare_transport_events_listener(z_moved_transport_events_listener_t *listener); /** * Constructs default value for :c:type:`z_link_events_listener_options_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_link_events_listener_options_default(z_link_events_listener_options_t *options); /** * Declares a link events listener. * * Parameters: * zs: Pointer to :c:type:`z_loaned_session_t`. * listener: Uninitialized location where listener will be constructed. * callback: Moved :c:type:`z_owned_closure_link_event_t`. * options: Optional :c:type:`z_link_events_listener_options_t`. * * Return: * ``0`` on success, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_declare_link_events_listener(const z_loaned_session_t *zs, z_owned_link_events_listener_t *listener, z_moved_closure_link_event_t *callback, z_link_events_listener_options_t *options); /** * Declares a background link events listener. * * The listener runs in background and cannot be undeclared explicitly. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_declare_background_link_events_listener(const z_loaned_session_t *zs, z_moved_closure_link_event_t *callback, z_link_events_listener_options_t *options); /** * Undeclares a link events listener. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_undeclare_link_events_listener(z_moved_link_events_listener_t *listener); /** * Gets a transport remote Zenoh ID. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_id_t z_transport_zid(const z_loaned_transport_t *transport); /** * Gets a transport remote entity kind. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_whatami_t z_transport_whatami(const z_loaned_transport_t *transport); /** * Returns whether QoS is enabled for this transport. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_transport_is_qos(const z_loaned_transport_t *transport); /** * Returns whether this transport is multicast. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_transport_is_multicast(const z_loaned_transport_t *transport); /** * Returns whether shared memory is enabled for this transport. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_transport_is_shm(const z_loaned_transport_t *transport); /** * Gets a link remote Zenoh ID. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_id_t z_link_zid(const z_loaned_link_t *link); /** * Gets a link source endpoint string. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_link_src(const z_loaned_link_t *link, z_owned_string_t *str_out); /** * Gets a link destination endpoint string. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_result_t z_link_dst(const z_loaned_link_t *link, z_owned_string_t *str_out); /** * Gets a link MTU. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ uint16_t z_link_mtu(const z_loaned_link_t *link); /** * Returns whether the link is stream-based. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_link_is_streamed(const z_loaned_link_t *link); /** * Returns whether the link transport is reliable. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_link_is_reliable(const z_loaned_link_t *link); /** * Gets a link group string. * * Parameters: * link: Pointer to a :c:type:`z_loaned_link_t`. * str_out: Pointer to an uninitialized :c:type:`z_owned_string_t` to store the result. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_link_group(const z_loaned_link_t *link, z_owned_string_t *str_out); /** * Gets a link auth identifier string. * * Parameters: * link: Pointer to a :c:type:`z_loaned_link_t`. * str_out: Pointer to an uninitialized :c:type:`z_owned_string_t` to store the result. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_link_auth_identifier(const z_loaned_link_t *link, z_owned_string_t *str_out); /** * Gets link interfaces. * * Parameters: * link: Pointer to a :c:type:`z_loaned_link_t`. * interfaces_out: Pointer to an uninitialized :c:type:`z_owned_string_array_t` to store the result. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ void z_link_interfaces(const z_loaned_link_t *link, z_owned_string_array_t *interfaces_out); /** * Gets link priority range. * * Parameters: * link: Pointer to a :c:type:`z_loaned_link_t`. * min_out: Pointer to store the minimum priority value. * max_out: Pointer to store the maximum priority value. * * Return: * ``true`` if link has priority information, ``false`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_link_priorities(const z_loaned_link_t *link, uint8_t *min_out, uint8_t *max_out); /** * Gets link reliability. * * Parameters: * link: Pointer to a :c:type:`z_loaned_link_t`. * reliability_out: Pointer to store the reliability value. * * Return: * ``true`` if link has reliability information, ``false`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ bool z_link_reliability(const z_loaned_link_t *link, z_reliability_t *reliability_out); /** * Gets transport event kind. * * Returns ``Z_SAMPLE_KIND_PUT`` when a transport is connected and ``Z_SAMPLE_KIND_DELETE`` when disconnected. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_sample_kind_t z_transport_event_kind(const z_loaned_transport_event_t *event); /** * Gets transport event transport. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ const z_loaned_transport_t *z_transport_event_transport(const z_loaned_transport_event_t *event); /** * Gets mutable transport event transport. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_loaned_transport_t *z_transport_event_transport_mut(z_loaned_transport_event_t *event); /** * Gets link event kind. * * Returns ``Z_SAMPLE_KIND_PUT`` when a link is added and ``Z_SAMPLE_KIND_DELETE`` when removed. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_sample_kind_t z_link_event_kind(const z_loaned_link_event_t *event); /** * Gets link event link. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ const z_loaned_link_t *z_link_event_link(const z_loaned_link_event_t *event); /** * Gets mutable link event link. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ z_loaned_link_t *z_link_event_link_mut(z_loaned_link_event_t *event); #endif /** * Converts a Zenoh ID into a string for print purposes. * * Parameters: * id: Pointer to the id to convert. * str: Pointer to uninitialized :c:type:`z_owned_string_t` to store the string. * * Return: * ``0`` if operation is successful, ``negative value`` otherwise. */ z_result_t z_id_to_string(const z_id_t *id, z_owned_string_t *str); /** * Gets the keyexpr from a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. */ const z_loaned_keyexpr_t *z_sample_keyexpr(const z_loaned_sample_t *sample); /** * Gets the payload of a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the payload from. * * Return: * The payload wrapped as a :c:type:`z_loaned_bytes_t`. */ const z_loaned_bytes_t *z_sample_payload(const z_loaned_sample_t *sample); /** * Gets the timestamp of a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the timestamp from. * * Return: * The pointer to timestamp wrapped as a :c:type:`z_timestamp_t`. Returns NULL if no timestamp was set. */ const z_timestamp_t *z_sample_timestamp(const z_loaned_sample_t *sample); /** * Gets the encoding of a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the encoding from. * * Return: * The encoding wrapped as a :c:type:`z_loaned_encoding_t`. */ const z_loaned_encoding_t *z_sample_encoding(const z_loaned_sample_t *sample); /** * Gets the kind of a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the kind from. * * Return: * The sample kind wrapped as a :c:type:`z_sample_kind_t`. */ z_sample_kind_t z_sample_kind(const z_loaned_sample_t *sample); #ifdef Z_FEATURE_UNSTABLE_API /** * Gets the reliability a sample was received with (unstable). * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the reliability from. * * Return: * The reliability wrapped as a :c:type:`z_reliability_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_reliability_t z_sample_reliability(const z_loaned_sample_t *sample); /** * Gets the source info for the sample (unstable). * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the source info from. * * Return: * The source info wrapped as a :c:type:`z_source_info_t`. Will return NULL if source info was not set by sender. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ const z_source_info_t *z_sample_source_info(const z_loaned_sample_t *sample); #endif /** * Got a sample qos congestion control value. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the congestion control from. * * Return: * The congestion control wrapped as a :c:type:`z_congestion_control_t`. */ z_congestion_control_t z_sample_congestion_control(const z_loaned_sample_t *sample); /** * Got whether sample qos express flag was set or not. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the express flag from. * * Return: * The express flag value. */ bool z_sample_express(const z_loaned_sample_t *sample); /** * Gets sample qos priority value. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the qos priority from. * * Return: * The priority wrapped as a :c:type:`z_priority_t`. */ z_priority_t z_sample_priority(const z_loaned_sample_t *sample); /** * Gets the attachment of a sample by aliasing it. * * Parameters: * sample: Pointer to a :c:type:`z_loaned_sample_t` to get the attachment from. * * Return: * Pointer to the attachment as a :c:type:`z_loaned_bytes_t`. */ const z_loaned_bytes_t *z_sample_attachment(const z_loaned_sample_t *sample); #if Z_FEATURE_PUBLICATION == 1 /** * Builds a :c:type:`z_put_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_put_options_t`. */ void z_put_options_default(z_put_options_t *options); /** * Builds a :c:type:`z_delete_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_delete_options_t`. */ void z_delete_options_default(z_delete_options_t *options); /** * Puts data for a given keyexpr. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to put the data through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to put the data for. * payload: Moved :c:type:`z_owned_bytes_t` containing the data to put. * options: Pointer to a :c:type:`z_put_options_t` to configure the operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_put(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_bytes_t *payload, const z_put_options_t *options); /** * Deletes data for a given keyexpr. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to delete the data through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to delete the data for. * options: Pointer to a :c:type:`z_delete_options_t` to configure the operation. * * Return: * ``0`` if delete operation is successful, ``negative value`` otherwise. */ z_result_t z_delete(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const z_delete_options_t *options); /** * Builds a :c:type:`z_publisher_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_delete_options_t`. */ void z_publisher_options_default(z_publisher_options_t *options); /** * Declares a publisher for a given keyexpr. * * Data can be put and deleted with this publisher with the help of the * :c:func:`z_publisher_put` and :c:func:`z_publisher_delete` functions. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the publisher through. * pub: Pointer to an uninitialized :c:type:`z_owned_publisher_t`. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the publisher with. * options: Pointer to a :c:type:`z_publisher_options_t` to configure the operation. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. */ z_result_t z_declare_publisher(const z_loaned_session_t *zs, z_owned_publisher_t *pub, const z_loaned_keyexpr_t *keyexpr, const z_publisher_options_t *options); /** * Undeclares the publisher. * * Parameters: * pub: Moved :c:type:`z_owned_publisher_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. */ z_result_t z_undeclare_publisher(z_moved_publisher_t *pub); /** * Builds a :c:type:`z_publisher_put_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_publisher_put_options_t`. */ void z_publisher_put_options_default(z_publisher_put_options_t *options); /** * Builds a :c:type:`z_publisher_delete_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_publisher_delete_options_t`. */ void z_publisher_delete_options_default(z_publisher_delete_options_t *options); /** * Puts data for the keyexpr bound to the given publisher. * * Parameters: * pub: Pointer to a :c:type:`z_loaned_publisher_t` from where to put the data. * payload: Moved :c:type:`z_owned_bytes_t` containing the data to put. * options: Pointer to a :c:type:`z_publisher_put_options_t` to configure the operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_publisher_put(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options); #if Z_FEATURE_ADVANCED_PUBLICATION == 1 z_result_t _z_publisher_put_impl(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options, _ze_advanced_cache_t *cache); #else z_result_t _z_publisher_put_impl(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options); #endif /** * Deletes data from the keyexpr bound to the given publisher. * * Parameters: * pub: Pointer to a :c:type:`z_loaned_publisher_t` from where to delete the data. * options: Pointer to a :c:type:`z_publisher_delete_options_t` to configure the delete operation. * * Return: * ``0`` if delete operation is successful, ``negative value`` otherwise. */ z_result_t z_publisher_delete(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options); #if Z_FEATURE_ADVANCED_PUBLICATION == 1 z_result_t _z_publisher_delete_impl(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options, _ze_advanced_cache_t *cache); #else z_result_t _z_publisher_delete_impl(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options); #endif /** * Gets the keyexpr from a publisher. * * Parameters: * publisher: Pointer to a :c:type:`z_loaned_publisher_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. */ const z_loaned_keyexpr_t *z_publisher_keyexpr(const z_loaned_publisher_t *publisher); #if defined(Z_FEATURE_UNSTABLE_API) /** * Gets the entity global Id from a publisher. * * Parameters: * publisher: Pointer to a :c:type:`z_loaned_publisher_t` to get the entity global Id from. * * Return: * The entity gloabl Id wrapped as a :c:type:`z_entity_global_id_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_entity_global_id_t z_publisher_id(const z_loaned_publisher_t *publisher); #endif #if Z_FEATURE_MATCHING == 1 /** * Declares a matching listener, registering a callback for notifying subscribers matching with a given publisher. * The callback will be run in the background until the corresponding publisher is dropped. * * Parameters: * publisher: A publisher to associate with matching listener. * callback: A closure that will be called every time the matching status of the publisher changes (If last subscriber * disconnects or when the first subscriber connects). * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t z_publisher_declare_background_matching_listener(const z_loaned_publisher_t *publisher, z_moved_closure_matching_status_t *callback); /** * Constructs matching listener, registering a callback for notifying subscribers matching with a given publisher. * * Parameters: * publisher: A publisher to associate with matching listener. * matching_listener: An uninitialized memory location where matching listener will be constructed. The matching * listener's callback will be automatically dropped when the publisher is dropped. callback: A closure that will be * called every time the matching status of the publisher changes (If last subscriber disconnects or when the first * subscriber connects). * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t z_publisher_declare_matching_listener(const z_loaned_publisher_t *publisher, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback); /** * Gets publisher matching status - i.e. if there are any subscribers matching its key expression. * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t z_publisher_get_matching_status(const z_loaned_publisher_t *publisher, z_matching_status_t *matching_status); /** * Undeclares the matching listener. * * Parameters: * listener: Moved :c:type:`z_owned_matching_listener_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. */ z_result_t z_undeclare_matching_listener(z_moved_matching_listener_t *listener); #endif // Z_FEATURE_MATCHING == 1 #endif // Z_FEATURE_PUBLICATION == 1 #if Z_FEATURE_QUERY == 1 /** * Builds a :c:type:`z_get_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_get_options_t`. */ void z_get_options_default(z_get_options_t *options); /** * Sends a distributed query for a given keyexpr. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to send the query through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to send the query for. * parameters: Pointer to the parameters as a null-terminated string. * callback: Moved :c:type:`z_owned_closure_reply_t` callback. * options: Pointer to a :c:type:`z_get_options_t` to configure the operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_get(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const char *parameters, z_moved_closure_reply_t *callback, z_get_options_t *options); /** * Sends a distributed query for a given keyexpr. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to send the query through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to send the query for. * parameters: Pointer to the parameters string. * parameters_len: Length of the parameters string. * callback: Moved :c:type:`z_owned_closure_reply_t` callback. * options: Pointer to a :c:type:`z_get_options_t` to configure the operation. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_get_with_parameters_substr(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const char *parameters, size_t parameters_len, z_moved_closure_reply_t *callback, z_get_options_t *options); /** * Constructs the default value for :c:type:`z_querier_get_options_t`. */ void z_querier_get_options_default(z_querier_get_options_t *options); /** * Constructs the default value for :c:type:`z_querier_options_t`. */ void z_querier_options_default(z_querier_options_t *options); /** * Constructs and declares a querier on the given key expression. * * The queries can be send with the help of the `z_querier_get()` function. * * Parameters: * zs: The Zenoh session. * querier: An uninitialized location in memory where querier will be constructed. * keyexpr: The key expression to send queries on. * options: Additional options for the querier. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_declare_querier(const z_loaned_session_t *zs, z_owned_querier_t *querier, const z_loaned_keyexpr_t *keyexpr, z_querier_options_t *options); /** * Frees memory and resets querier to its gravestone state. */ z_result_t z_undeclare_querier(z_moved_querier_t *querier); /** * Query data from the matching queryables in the system. * * Replies are provided through a callback function. * * Parameters: * querier: The querier to make query from. * parameters: The query's parameters null-terminated string, similar to a url's query segment. * callback: The callback function that will be called on reception of replies for this query. It will be * automatically dropped once all replies are processed. * options: Additional options for the get. All owned fields will be consumed. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_querier_get(const z_loaned_querier_t *querier, const char *parameters, z_moved_closure_reply_t *callback, z_querier_get_options_t *options); /** * Query data from the matching queryables in the system. * * Replies are provided through a callback function. * * Parameters: * querier: The querier to make query from. * parameters: The query's parameters string, similar to a url's query segment. * parameters_len: Length of the parameters string * callback: The callback function that will be called on reception of replies for this query. It will be * automatically dropped once all replies are processed. * options: Additional options for the get. All owned fields will be consumed. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_querier_get_with_parameters_substr(const z_loaned_querier_t *querier, const char *parameters, size_t parameters_len, z_moved_closure_reply_t *callback, z_querier_get_options_t *options); /** * Returns the key expression of the querier. */ const z_loaned_keyexpr_t *z_querier_keyexpr(const z_loaned_querier_t *querier); #if defined(Z_FEATURE_UNSTABLE_API) /** * Gets the entity global Id from a querier. * * Parameters: * publisher: Pointer to a :c:type:`z_loaned_querier_t` to get the entity global Id from. * * Return: * The entity gloabl Id wrapped as a :c:type:`z_entity_global_global_id_t`. */ z_entity_global_id_t z_querier_id(const z_loaned_querier_t *querier); #endif #if Z_FEATURE_MATCHING == 1 /** * Declares a matching listener, registering a callback for notifying queryables matching the given querier key * expression and target. The callback will be run in the background until the corresponding querier is dropped. * * Parameters: * querier: A querier to associate with matching listener. * callback: A closure that will be called every time the matching status of the querier changes (If last * queryable disconnects or when the first queryable connects). * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_querier_declare_background_matching_listener(const z_loaned_querier_t *querier, z_moved_closure_matching_status_t *callback); /** * Constructs matching listener, registering a callback for notifying queryables matching with a given querier's * key expression and target. * * Parameters: * querier: A querier to associate with matching listener. * matching_listener: An uninitialized memory location where matching listener will be constructed. The matching * listener's callback will be automatically dropped when the querier is dropped. * callback: A closure that will be called every time the matching status of the querier changes (If last * queryable disconnects or when the first queryable connects). * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_querier_declare_matching_listener(const z_loaned_querier_t *querier, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback); /** * Gets querier matching status - i.e. if there are any queryables matching its key expression and target. * * Return: * ``0`` if put operation is successful, ``negative value`` otherwise. */ z_result_t z_querier_get_matching_status(const z_loaned_querier_t *querier, z_matching_status_t *matching_status); #endif // Z_FEATURE_MATCHING == 1 /** * Checks if queryable answered with an OK, which allows this value to be treated as a sample. * * Parameters: * reply: Pointer to a :c:type:`z_loaned_reply_t` to check. * * Return: * ``true`` if queryable answered with an OK, ``false`` otherwise. */ bool z_reply_is_ok(const z_loaned_reply_t *reply); /** * Gets the content of an OK reply. * * You should always make sure that :c:func:`z_reply_is_ok` returns ``true`` before calling this function. * * Parameters: * reply: Pointer to a :c:type:`z_loaned_reply_t` to get content from. * * Return: * The OK reply content wrapped as a :c:type:`z_loaned_sample_t`. */ const z_loaned_sample_t *z_reply_ok(const z_loaned_reply_t *reply); /** * Gets the contents of an error reply. * * You should always make sure that :c:func:`z_reply_is_ok` returns ``false`` before calling this function. * * Parameters: * reply: Pointer to a :c:type:`z_loaned_reply_t` to get content from. * * Return: * The error reply content wrapped as a :c:type:`z_loaned_reply_err_t`. */ const z_loaned_reply_err_t *z_reply_err(const z_loaned_reply_t *reply); #ifdef Z_FEATURE_UNSTABLE_API /** * Gets the id of the zenoh instance that answered this Reply. * * Parameters: * reply: Pointer to a :c:type:`z_loaned_reply_t` to get content from. * * Return: * `true` if id is present */ bool z_reply_replier_id(const z_loaned_reply_t *reply, z_entity_global_id_t *out_id); #endif // Z_FEATURE_UNSTABLE_API #endif // Z_FEATURE_QUERY == 1 #if Z_FEATURE_QUERYABLE == 1 /** * Builds a :c:type:`z_queryable_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_queryable_options_t`. */ void z_queryable_options_default(z_queryable_options_t *options); /** * Declares a queryable for a given keyexpr. * Note that dropping queryable drops its callback. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the subscriber through. * queryable: Pointer to an uninitialized :c:type:`z_owned_queryable_t` to contain the queryable. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the subscriber with. * callback: Pointer to a :c:type:`z_owned_closure_query_t` callback. * options: Pointer to a :c:type:`z_queryable_options_t` to configure the declare. * * Return: * ``0`` if declare operation is successful, ``negative value`` otherwise. */ z_result_t z_declare_queryable(const z_loaned_session_t *zs, z_owned_queryable_t *queryable, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_query_t *callback, const z_queryable_options_t *options); /** * Undeclares the queryable. * * Parameters: * pub: Moved :c:type:`z_owned_queryable_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. */ z_result_t z_undeclare_queryable(z_moved_queryable_t *pub); /** * Declares a background queryable for a given keyexpr. The queryable callback will be called * to proccess incoming queries until the corresponding session is closed or dropped. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the subscriber through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the subscriber with. * callback: Pointer to a :c:type:`z_owned_closure_query_t` callback. * options: Pointer to a :c:type:`z_queryable_options_t` to configure the declare. * * Return: * ``0`` if declare operation is successful, ``negative value`` otherwise. */ z_result_t z_declare_background_queryable(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_query_t *callback, const z_queryable_options_t *options); /** * Builds a :c:type:`z_query_reply_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_query_reply_options_t`. */ void z_query_reply_options_default(z_query_reply_options_t *options); /** * Sends a reply to a query. * * This function must be called inside of a :c:type:`z_owned_closure_query_t` callback associated to the * :c:type:`z_owned_queryable_t`, passing the received query as parameters of the callback function. This function can * be called multiple times to send multiple replies to a query. The reply will be considered complete when the callback * returns. * * Parameters: * query: Pointer to a :c:type:`z_loaned_query_t` to reply. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the reply with. * payload: Pointer to the reply data. * options: Pointer to a :c:type:`z_query_reply_options_t` to configure the reply. * * Return: * ``0`` if reply operation is successful, ``negative value`` otherwise. */ z_result_t z_query_reply(const z_loaned_query_t *query, const z_loaned_keyexpr_t *keyexpr, z_moved_bytes_t *payload, const z_query_reply_options_t *options); z_result_t _z_query_reply_sample(const z_loaned_query_t *query, z_loaned_sample_t *sample, const z_query_reply_options_t *options); z_result_t z_query_take_from_loaned(z_owned_query_t *dst, z_loaned_query_t *src); /** * Builds a :c:type:`z_query_reply_del_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_query_reply_del_options_t`. */ void z_query_reply_del_options_default(z_query_reply_del_options_t *options); /** * Sends a reply delete to a query. * * This function must be called inside of a :c:type:`z_owned_closure_query_t` callback associated to the * :c:type:`z_owned_queryable_t`, passing the received query as parameters of the callback function. This function can * be called multiple times to send multiple replies to a query. The reply will be considered complete when the callback * returns. * * Parameters: * query: Pointer to a :c:type:`z_loaned_query_t` to reply. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the reply with. * options: Pointer to a :c:type:`z_query_reply_del_options_t` to configure the reply. * * Return: * ``0`` if reply operation is successful, ``negative value`` otherwise. */ z_result_t z_query_reply_del(const z_loaned_query_t *query, const z_loaned_keyexpr_t *keyexpr, const z_query_reply_del_options_t *options); /** * Builds a :c:type:`z_query_reply_err_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_query_reply_err_options_t`. */ void z_query_reply_err_options_default(z_query_reply_err_options_t *options); /** * Sends a reply error to a query. * * This function must be called inside of a :c:type:`z_owned_closure_query_t` callback associated to the * :c:type:`z_owned_queryable_t`, passing the received query as parameters of the callback function. This function can * be called multiple times to send multiple replies to a query. The reply will be considered complete when the callback * returns. * * Parameters: * query: Pointer to a :c:type:`z_loaned_query_t` to reply. * payload: Moved reply error data payload. * options: Pointer to a :c:type:`z_query_reply_err_options_t` to configure the reply error. * * Return: * ``0`` if reply operation is successful, ``negative value`` otherwise. */ z_result_t z_query_reply_err(const z_loaned_query_t *query, z_moved_bytes_t *payload, const z_query_reply_err_options_t *options); #if defined(Z_FEATURE_UNSTABLE_API) /** * Gets the entity global Id from a queryable. * * Parameters: * publisher: Pointer to a :c:type:`z_loaned_queryable_t` to get the entity global Id from. * * Return: * The entity gloabl Id wrapped as a :c:type:`z_loaned_queryable_t`. */ z_entity_global_id_t z_queryable_id(const z_loaned_queryable_t *queryable); #endif /** * Gets the keyexpr from a queryable. * * Parameters: * queryable: Pointer to a :c:type:`z_loaned_queryable_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. Will return NULL if * corresponding session is closed or dropped. * The lifetime of key expression pointer is bound to those of queryable and its session. */ const z_loaned_keyexpr_t *z_queryable_keyexpr(const z_loaned_queryable_t *queryable); #endif /** * Builds a new keyexpr. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to store the keyexpr. * name: Pointer to the null-terminated string of the keyexpr. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_from_str(z_owned_keyexpr_t *keyexpr, const char *name); /** * Builds a new keyexpr from a substring. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to store the keyexpr. * name: Pointer to the start of the substring for keyxpr. * len: Length of the substring to consider. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_from_substr(z_owned_keyexpr_t *keyexpr, const char *name, size_t len); /** * Builds a :c:type:`z_owned_keyexpr_t` from a null-terminated string with auto canonization. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t`. * name: Pointer to string representation of the keyexpr as a null terminated string. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_from_str_autocanonize(z_owned_keyexpr_t *keyexpr, const char *name); /** * Builds a :c:type:`z_owned_keyexpr_t` from a substring with auto canonization. * * Parameters: * keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to store the keyexpr. * name: Pointer to the start of the substring for keyexpr. * len: Length of the substring to consider. After the function return it will be equal to the canonized key * expression string length. * * Return: * ``0`` if creation is successful, ``negative value`` otherwise. */ z_result_t z_keyexpr_from_substr_autocanonize(z_owned_keyexpr_t *keyexpr, const char *name, size_t *len); /** * Declares a keyexpr, so that it is mapped on a numerical id. * * This numerical id is used on the network to save bandwidth and ease the retrieval of the concerned resource * in the routing tables. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the keyexpr through. * declared_keyexpr: Pointer to an uninitialized :c:type:`z_owned_keyexpr_t` to contain the declared keyexpr. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the keyexpr with. * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. */ z_result_t z_declare_keyexpr(const z_loaned_session_t *zs, z_owned_keyexpr_t *declared_keyexpr, const z_loaned_keyexpr_t *keyexpr); /** * Undeclares a keyexpr. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to undeclare the data through. * keyexpr: Moved :c:type:`z_owned_keyexpr_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. */ z_result_t z_undeclare_keyexpr(const z_loaned_session_t *zs, z_moved_keyexpr_t *keyexpr); /** * Constructs a new empty string array. * * Parameters: * a: Pointer to an uninitialized :c:type:`z_owned_string_array_t` to store the array of strings. */ void z_string_array_new(z_owned_string_array_t *a); /** * Appends specified value to the end of the string array by alias. * * Parameters: * a: Pointer to :c:type:`z_loaned_string_array_t`. * value: Pointer to the string to be added. * * Return: * The new length of the array. */ size_t z_string_array_push_by_alias(z_loaned_string_array_t *a, const z_loaned_string_t *value); /** * Appends specified value to the end of the string array by copying. * * Parameters: * a: Pointer to :c:type:`z_loaned_string_array_t`. * value: Pointer to the string to be added. * * Return: * The new length of the array. */ size_t z_string_array_push_by_copy(z_loaned_string_array_t *a, const z_loaned_string_t *value); /** * Returns the value at the position of index in the string array. * * Parameters: * a: Pointer to :c:type:`z_loaned_string_array_t`. * k: index value. * * Return: * `NULL` if the index is out of bounds. */ const z_loaned_string_t *z_string_array_get(const z_loaned_string_array_t *a, size_t k); /** * Returns the number of elements in the array. */ size_t z_string_array_len(const z_loaned_string_array_t *a); /** * Returns ``true`` if the array is empty, ``false`` otherwise. */ bool z_string_array_is_empty(const z_loaned_string_array_t *a); #if Z_FEATURE_SUBSCRIPTION == 1 /** * Builds a :c:type:`z_subscriber_options_t` with default values. * * Parameters: * options: Pointer to an uninitialized :c:type:`z_subscriber_options_t`. */ void z_subscriber_options_default(z_subscriber_options_t *options); /** * Declares a subscriber for a given keyexpr. * Note that dropping subscriber drops its callback. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the subscriber through. * sub: Pointer to a :c:type:`z_owned_subscriber_t` to contain the subscriber. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the subscriber with. * callback: Pointer to a`z_owned_closure_sample_t` callback. * options: Pointer to a :c:type:`z_subscriber_options_t` to configure the operation * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. */ z_result_t z_declare_subscriber(const z_loaned_session_t *zs, z_owned_subscriber_t *sub, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, const z_subscriber_options_t *options); /** * Undeclares the subscriber. * * Parameters: * pub: Moved :c:type:`z_owned_subscriber_t` to undeclare. * * Return: * ``0`` if undeclare is successful, ``negative value`` otherwise. */ z_result_t z_undeclare_subscriber(z_moved_subscriber_t *pub); /** * Declares a background subscriber for a given keyexpr. Subscriber callback will be called to process the messages, * until the corresponding session is closed or dropped. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to declare the subscriber through. * keyexpr: Pointer to a :c:type:`z_loaned_keyexpr_t` to bind the subscriber with. * callback: Pointer to a`z_owned_closure_sample_t` callback. * options: Pointer to a :c:type:`z_subscriber_options_t` to configure the operation * * Return: * ``0`` if declare is successful, ``negative value`` otherwise. */ z_result_t z_declare_background_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, const z_subscriber_options_t *options); /** * Gets the keyexpr from a subscriber. * * Parameters: * subscriber: Pointer to a :c:type:`z_loaned_subscriber_t` to get the keyexpr from. * * Return: * The keyexpr wrapped as a :c:type:`z_loaned_keyexpr_t`. Will return `NULL` if corresponding session * is closed or dropped. * The lifetime of key expression pointer is bound to those of subscriber and its session. */ const z_loaned_keyexpr_t *z_subscriber_keyexpr(const z_loaned_subscriber_t *subscriber); #if defined(Z_FEATURE_UNSTABLE_API) /** * Gets the entity global Id from a subscriber. * * Parameters: * subscriber: Pointer to a :c:type:`z_loaned_subscriber_t` to get the entity global Id from. * * Return: * The entity gloabl Id wrapped as a :c:type:`z_entity_global_global_id_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_entity_global_id_t z_subscriber_id(const z_loaned_subscriber_t *subscriber); #endif #endif #if Z_FEATURE_BATCHING == 1 /** * Activate the batching mechanism, any message that would have been sent on the network by a subsequent api call (e.g * z_put, z_get) will be instead stored until either: the batch is full, flushed with :c:func:`zp_batch_flush`, batching * is stopped with :c:func:`zp_batch_stop`, a message needs to be sent immediately. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` that will start batching messages. * * Return: * ``0`` if batching started, ``negative value`` otherwise. */ z_result_t zp_batch_start(const z_loaned_session_t *zs); /** * Send the currently batched messages on the network. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` that will send its batched messages. * * Return: * ``0`` if batch successfully sent, ``negative value`` otherwise. */ z_result_t zp_batch_flush(const z_loaned_session_t *zs); /** * Deactivate the batching mechanism and send the currently batched on the network. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` that will stop batching messages. * * Return: * ``0`` if batching stopped and batch successfully sent, ``negative value`` otherwise. */ z_result_t zp_batch_stop(const z_loaned_session_t *zs); #endif #if Z_FEATURE_MULTI_THREAD == 1 || defined(SPHINX_DOCS) /************* Multi Thread Tasks helpers **************/ /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * options: Pointer to an uninitialized :c:type:`zp_task_read_options_t`. */ void zp_task_read_options_default(zp_task_read_options_t *options); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to start the task from. * options: Pointer to a :c:type:`zp_task_read_options_t` to configure the task. * * Return: * ``0`` if task started successfully, ``negative value`` otherwise. */ z_result_t zp_start_read_task(z_loaned_session_t *zs, const zp_task_read_options_t *options); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to stop the task from. * * Return: * ``0`` if task stopped successfully, ``negative value`` otherwise. */ z_result_t zp_stop_read_task(z_loaned_session_t *zs); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. */ bool zp_read_task_is_running(const z_loaned_session_t *zs); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * options: Pointer to an uninitialized :c:type:`zp_task_lease_options_t`. */ void zp_task_lease_options_default(zp_task_lease_options_t *options); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to start the task from. * options: Pointer to a :c:type:`zp_task_lease_options_t` to configure the task. * * Return: * ``0`` if task started successfully, ``negative value`` otherwise. */ z_result_t zp_start_lease_task(z_loaned_session_t *zs, const zp_task_lease_options_t *options); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to stop the task from. * * Return: * ``0`` if task stopped successfully, ``negative value`` otherwise. */ z_result_t zp_stop_lease_task(z_loaned_session_t *zs); /** * Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started automatically when session is * created. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. */ bool zp_lease_task_is_running(const z_loaned_session_t *zs); #endif #if Z_FEATURE_MULTI_THREAD == 0 || defined(SPHINX_DOCS) /************* Single Thread helpers **************/ /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * options: Pointer to an uninitialized :c:type:`zp_read_options_t`. */ void zp_read_options_default(zp_read_options_t *options); /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to execute the read for. * options: Pointer to a :c:type:`zp_read_options_t` to configure the operation. * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t zp_read(const z_loaned_session_t *zs, const zp_read_options_t *options); /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * options: Pointer to an uninitialized :c:type:`zp_send_keep_alive_options_t`. */ void zp_send_keep_alive_options_default(zp_send_keep_alive_options_t *options); /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to execute the send for. * options: Pointer to a :c:type:`zp_send_keep_alive_options_t` to configure the operation. * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t zp_send_keep_alive(const z_loaned_session_t *zs, const zp_send_keep_alive_options_t *options); /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * options: Pointer to an uninitialized :c:type:`zp_send_join_options_t`. */ void zp_send_join_options_default(zp_send_join_options_t *options); /** * Deprecated, please use :c:func:`zp_spin_once` instead, which will run a single pending zenoh task * (including read, lease, keep alive, accept, connect, etc...) from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to execute the send for. * options: Pointer to a :c:type:`zp_send_keep_alive_options_t` to configure the operation. * * Return: * ``0`` if execution was successful, ``negative value`` otherwise. */ z_result_t zp_send_join(const z_loaned_session_t *zs, const zp_send_join_options_t *options); /** * Spins zenoh executor once, executing a single pending task (such as read, lease, keep alive, accept, connect, etc...) * from the task queue. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. * * Parameters: * zs: Pointer to a :c:type:`z_loaned_session_t` to spin executor for. */ void zp_spin_once(const z_loaned_session_t *zs); #endif #ifdef Z_FEATURE_UNSTABLE_API /** * Gets the default reliability value (unstable). * * Return: * The reliability wrapped as a :c:type:`z_reliability_t`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_reliability_t z_reliability_default(void); #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_QUERY == 1 /** * Construct a new cancellation token. Can be used to interrupt get operations. (unstable). * * Parameters: * cancellation_token: Pointer to an uninitialized :c:type:`z_owned_cancellation_token_t`. * * Return: * ``0`` if creation successful, ``negative value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t z_cancellation_token_new(z_owned_cancellation_token_t *cancellation_token); /** * Interrupt all currently running get calls, to which clones of the token were passed. (unstable). * * Parameters: * cancellation_token: Pointer to token to cancel. * * Return: * ``0`` in case of success (in this case all pending operations are guaranteed to be cancelled or terminated), * ``negative error value`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ z_result_t z_cancellation_token_cancel(z_loaned_cancellation_token_t *cancellation_token); /** * Verify if token was cancelled (unstable). * * Parameters: * cancellation_token: Pointer to cancellation token. * * Return: * ``true`` if token was cancelled (i.e if :c:func:`z_cancellation_token_cancel` was called on it or one of its * clones), ``false`` otherwise. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ bool z_cancellation_token_is_cancelled(const z_loaned_cancellation_token_t *cancellation_token); #endif #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_API_PRIMITIVES_H */ ================================================ FILE: include/zenoh-pico/api/serialization.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_API_SERIALIZATION_H #define INCLUDE_ZENOH_PICO_API_SERIALIZATION_H #include "olv_macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/utils/endianness.h" #ifdef __cplusplus extern "C" { #endif /** * Represents a reader for serialized data. */ typedef struct ze_deserializer_t { z_bytes_reader_t _reader; } ze_deserializer_t; typedef struct _ze_serializer_t { _z_bytes_writer_t _writer; } _ze_serializer_t; /** * Represents a writer for serialized data. */ _Z_OWNED_TYPE_VALUE_PREFIX(ze, _ze_serializer_t, serializer) /** * Constructs an empty serializer. * * Parameters: * serializer: An uninitialized memory location where serializer is to be constructed. * * Return: * ``0`` in case of success, ``negative value`` otherwise. */ z_result_t ze_serializer_empty(ze_owned_serializer_t *serializer); /** * Finishes serialization and returns underlying bytes. * * Parameters: * serializer: A data serializer. * bytes: An uninitialized memory location where bytes is to be constructed. */ void ze_serializer_finish(ze_moved_serializer_t *serializer, z_owned_bytes_t *bytes); /** * Returns a deserializer for :c:type:`z_loaned_bytes_t`. * * The `bytes` should outlive the reader and should not be modified, while reader is in use. * * Parameters: * bytes: Data to deserialize. * * Return: * The constructed :c:type:`ze_deserializer_t`. */ ze_deserializer_t ze_deserializer_from_bytes(const z_loaned_bytes_t *bytes); /** * Checks if deserializer parsed all of its data. * * Parameters: * deserializer: A deserializer instance. * * Return: * ``True`` if there is no more data to parse, ``false`` otherwise. */ bool ze_deserializer_is_done(const ze_deserializer_t *deserializer); /** * Writes a serialized `uint8_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `uint8_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_uint8(ze_loaned_serializer_t *serializer, uint8_t val) { return z_bytes_writer_write_all(&serializer->_writer, &val, 1); } /** * Writes a serialized `uint16_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `uint16_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_uint16(ze_loaned_serializer_t *serializer, uint16_t val) { _z_host_le_store16(val, (uint8_t *)&val); return z_bytes_writer_write_all(&serializer->_writer, (uint8_t *)&val, sizeof(val)); } /** * Writes a serialized `uint32_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `uint32_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_uint32(ze_loaned_serializer_t *serializer, uint32_t val) { _z_host_le_store32(val, (uint8_t *)&val); return z_bytes_writer_write_all(&serializer->_writer, (uint8_t *)&val, sizeof(val)); } /** * Writes a serialized `uint64_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `uint64_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_uint64(ze_loaned_serializer_t *serializer, uint64_t val) { _z_host_le_store64(val, (uint8_t *)&val); return z_bytes_writer_write_all(&serializer->_writer, (uint8_t *)&val, sizeof(val)); } /** * Writes a serialized `float` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `float` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_float(ze_loaned_serializer_t *serializer, float val) { return z_bytes_writer_write_all(&serializer->_writer, (uint8_t *)&val, sizeof(val)); } /** * Writes a serialized `double` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `double` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_double(ze_loaned_serializer_t *serializer, double val) { return z_bytes_writer_write_all(&serializer->_writer, (uint8_t *)&val, sizeof(val)); } /** * Writes a serialized 'bool' into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `bool` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_bool(ze_loaned_serializer_t *serializer, bool val) { return ze_serializer_serialize_uint8(serializer, val ? 1 : 0); } /** * Writes a serialized `int8_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `int8_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_int8(ze_loaned_serializer_t *serializer, int8_t val) { return ze_serializer_serialize_uint8(serializer, (uint8_t)val); } /** * Writes a serialized `int16_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `int16_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_int16(ze_loaned_serializer_t *serializer, int16_t val) { return ze_serializer_serialize_uint16(serializer, (uint16_t)val); } /** * Writes a serialized `int32_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `int32_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_int32(ze_loaned_serializer_t *serializer, int32_t val) { return ze_serializer_serialize_uint32(serializer, (uint32_t)val); } /** * Writes a serialized `int64_t` into underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: `int64_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ static inline z_result_t ze_serializer_serialize_int64(ze_loaned_serializer_t *serializer, int64_t val) { return ze_serializer_serialize_uint64(serializer, (uint64_t)val); } /** * Deserializes next portion of data into a `uint8_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `uint8_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_uint8(ze_deserializer_t *deserializer, uint8_t *val) { if (z_bytes_reader_read(&deserializer->_reader, val, 1) != 1) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } return _Z_RES_OK; } /** * Deserializes next portion of data into a `uint16_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `uint16_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_uint16(ze_deserializer_t *deserializer, uint16_t *val) { if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val, sizeof(uint16_t)) != sizeof(uint16_t)) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } *val = _z_host_le_load16((const uint8_t *)val); return _Z_RES_OK; } /** * Deserializes next portion of data into a `uint32_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `uint32_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_uint32(ze_deserializer_t *deserializer, uint32_t *val) { if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val, sizeof(uint32_t)) != sizeof(uint32_t)) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } *val = _z_host_le_load32((uint8_t *)val); return _Z_RES_OK; } /** * Deserializes next portion of data into a `uint64_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `uint64_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_uint64(ze_deserializer_t *deserializer, uint64_t *val) { if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val, sizeof(uint64_t)) != sizeof(uint64_t)) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } *val = _z_host_le_load64((uint8_t *)val); return _Z_RES_OK; } /** * Deserializes next portion of data into a `float`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `float` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_float(ze_deserializer_t *deserializer, float *val) { if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val, sizeof(float)) != sizeof(float)) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } return _Z_RES_OK; } /** * Deserializes next portion of data into a `double`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `double` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_double(ze_deserializer_t *deserializer, double *val) { if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val, sizeof(double)) != sizeof(double)) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } return _Z_RES_OK; } /** * Deserializes next portion of data into a `bool`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `bool` to contain the deserialized value. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_bool(ze_deserializer_t *deserializer, bool *val) { uint8_t res; _Z_RETURN_IF_ERR(ze_deserializer_deserialize_uint8(deserializer, &res)); if (res > 1) { return Z_EDESERIALIZE; } *val = (res == 1); return _Z_RES_OK; } /** * Deserializes next portion of data into a `int8_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `int8_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_int8(ze_deserializer_t *deserializer, int8_t *val) { return ze_deserializer_deserialize_uint8(deserializer, (uint8_t *)val); } /** * Deserializes next portion of data into a `int16_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `int16_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_int16(ze_deserializer_t *deserializer, int16_t *val) { return ze_deserializer_deserialize_uint16(deserializer, (uint16_t *)val); } /** * Deserializes next portion of data into a `int32_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `int32_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_int32(ze_deserializer_t *deserializer, int32_t *val) { return ze_deserializer_deserialize_uint32(deserializer, (uint32_t *)val); } /** * Deserializes next portion of data into a `int64_t`. * * Parameters: * deserializer: A deserializer instance. * dst: Pointer to an uninitialized `int64_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ static inline z_result_t ze_deserializer_deserialize_int64(ze_deserializer_t *deserializer, int64_t *val) { return ze_deserializer_deserialize_uint64(deserializer, (uint64_t *)val); } /** * Serializes array of bytes and writes it into an underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: Pointer to the data to serialize. * len: Number of bytes to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_buf(ze_loaned_serializer_t *serializer, const uint8_t *val, size_t len); /** * Serializes slice and writes it into an underlying :c:type:`z_owned_bytes_t`. * * Parameters: * serializer: A serializer instance. * val: A slice to serialize. * len: Number of bytes to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_slice(ze_loaned_serializer_t *serializer, const z_loaned_slice_t *val); /** * Deserializes next portion of data and advances the reader position. * * Parameters: * deserializer: A deserializer instance. * val: Pointer to an uninitialized :c:type:`z_owned_slice_t` to contain the deserialized slice. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_deserializer_deserialize_slice(ze_deserializer_t *deserializer, z_owned_slice_t *val); /** * Serializes a null-terminated string and writes it into an underlying :c:type:`z_owned_bytes_t`. * The string should be a valid UTF-8. * * Parameters: * serializer: A serializer instance. * val: Pointer to the string to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_str(ze_loaned_serializer_t *serializer, const char *val); /** * Serializes a substring and writes it into an underlying :c:type:`z_owned_bytes_t`. * The substring should be a valid UTF-8. * * Parameters: * serializer: A serializer instance. * start: Pointer to the start of the substring to serialize. * len: Length of the substring to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_substr(ze_loaned_serializer_t *serializer, const char *start, size_t len); /** * Serializes a string and writes it into an underlying :c:type:`z_owned_bytes_t`. * The string should be a valid UTF-8. * * Parameters: * serializer: A serializer instance. * val: Pointer to the string to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_string(ze_loaned_serializer_t *serializer, const z_loaned_string_t *val); /** * Deserializes next portion of data and advances the reader position. * * Parameters: * deserializer: A deserializer instance. * val: Pointer to an uninitialized :c:type:`z_owned_string_t` to contain the deserialized string. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_deserializer_deserialize_string(ze_deserializer_t *deserializer, z_owned_string_t *val); /** * Initiate serialization of a sequence of multiple elements. * * Parameters: * serializer: A serializer instance. * len: Length of the sequence. Could be read during deserialization using * :c:func:`ze_deserializer_deserialize_sequence_length`. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_serializer_serialize_sequence_length(ze_loaned_serializer_t *serializer, size_t len); /** * Initiate deserialization of a sequence of multiple elements. * * Parameters: * deserializer: A deserializer instance. * len: A pointer where the length of the sequence (previously passed via * :c:func:`ze_serializer_serialize_sequence_length`) will be written. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_deserializer_deserialize_sequence_length(ze_deserializer_t *deserializer, size_t *len); /** * Serializes data into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized data. * data: Pointer to the data to serialize. * len: Number of bytes to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len); /** * Serializes a string into a :c:type:`z_owned_bytes_t`. * * The string should be a valid UTF-8. * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized string. * s: Pointer to the string to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_string(z_owned_bytes_t *bytes, const z_loaned_string_t *s); /** * Serializes a null-terminated string into a :c:type:`z_owned_bytes_t`. * The string should be a valid UTF-8. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized string. * value: Pointer to the string to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_str(z_owned_bytes_t *bytes, const char *value); /** * Serializes a substring into a :c:type:`z_owned_bytes_t`. * The substring should be a valid UTF-8. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized string. * start: Pointer to the the start of string to serialize. * len: Length of the substring to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_substr(z_owned_bytes_t *bytes, const char *start, size_t len); /** * Serializes a slice into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized slice. * slice: Pointer to the slice to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_slice(z_owned_bytes_t *bytes, const z_loaned_slice_t *slice); /** * Serializes a `int8_t` into a :c:type:`z_owned_bytes_t` . * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `int8_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_int8(z_owned_bytes_t *bytes, int8_t val); /** * Serializes a `int16_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `int16_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_int16(z_owned_bytes_t *bytes, int16_t val); /** * Serializes a `int32_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `int32_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_int32(z_owned_bytes_t *bytes, int32_t val); /** * Serializes a `int64_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `int64_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_int64(z_owned_bytes_t *bytes, int64_t val); /** * Serializes a `uint8_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `uint8_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_uint8(z_owned_bytes_t *bytes, uint8_t val); /** * Serializes a `uint16_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `uint16_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_uint16(z_owned_bytes_t *bytes, uint16_t val); /** * Serializes a `uint32_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `uint32_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_uint32(z_owned_bytes_t *bytes, uint32_t val); /** * Serializes a `uint64_t` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `uint64_t` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_uint64(z_owned_bytes_t *bytes, uint64_t val); /** * Serializes a `float` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `float` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_float(z_owned_bytes_t *bytes, float val); /** * Serializes a `double` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `double` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_double(z_owned_bytes_t *bytes, double val); /** * Serializes a `bool` into a :c:type:`z_owned_bytes_t`. * * Parameters: * bytes: An uninitialized :c:type:`z_owned_bytes_t` to contain the serialized int. * val: `bool` value to serialize. * * Return: * ``0`` if serialization is successful, ``negative value`` otherwise. */ z_result_t ze_serialize_bool(z_owned_bytes_t *bytes, bool val); /** * Deserializes data into a :c:type:`z_owned_slice_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * str: Pointer to an uninitialized :c:type:`z_owned_slice_t` to contain the deserialized slice. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_slice(const z_loaned_bytes_t *bytes, z_owned_slice_t *dst); /** * Deserializes data into a :c:type:`z_owned_string_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * str: Pointer to an uninitialized :c:type:`z_owned_string_t` to contain the deserialized string. * * Return: * ``0`` if deserialization is successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_string(const z_loaned_bytes_t *bytes, z_owned_string_t *str); /** * Deserializes data into a `int8_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `int8_t` to contain the deserialized. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_int8(const z_loaned_bytes_t *bytes, int8_t *dst); /** * Deserializes data into a `int16_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `int16_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_int16(const z_loaned_bytes_t *bytes, int16_t *dst); /** * Deserializes data into a `int32_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `int32_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_int32(const z_loaned_bytes_t *bytes, int32_t *dst); /** * Deserializes data into a `int64_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `int64_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_int64(const z_loaned_bytes_t *bytes, int64_t *dst); /** * Deserializes data into a `uint8_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `uint8_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_uint8(const z_loaned_bytes_t *bytes, uint8_t *dst); /** * Deserializes data into a `uint16_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `uint16_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_uint16(const z_loaned_bytes_t *bytes, uint16_t *dst); /** * Deserializes data into a `uint32_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `uint32_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_uint32(const z_loaned_bytes_t *bytes, uint32_t *dst); /** * Deserializes data into a `uint64_t`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `uint64_t` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_uint64(const z_loaned_bytes_t *bytes, uint64_t *dst); /** * Deserializes data into a `float`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `float` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_float(const z_loaned_bytes_t *bytes, float *dst); /** * Deserializes data into a `double`. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `double` to contain the deserialized number. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_double(const z_loaned_bytes_t *bytes, double *dst); /** * Deserializes data into a boolean. * * Parameters: * bytes: Pointer to a :c:type:`z_loaned_bytes_t` to deserialize. * dst: Pointer to an uninitialized `bool` to contain the deserialized value. * * Return: * ``0`` if deserialization successful, or a ``negative value`` otherwise. */ z_result_t ze_deserialize_bool(const z_loaned_bytes_t *bytes, bool *dst); _Z_OWNED_FUNCTIONS_NO_COPY_DEF_PREFIX(ze, serializer) #ifdef __cplusplus } #endif #endif ================================================ FILE: include/zenoh-pico/api/types.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, // #ifndef INCLUDE_ZENOH_PICO_API_TYPES_H #define INCLUDE_ZENOH_PICO_API_TYPES_H #include "olv_macros.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/net/matching.h" #include "zenoh-pico/net/publish.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/reply.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/net/subscribe.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/cancellation.h" #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif /** * Represents a Zenoh ID. * * In general, valid Zenoh IDs are LSB-first 128bit unsigned and non-zero integers. * * Members: * uint8_t id[16]: The array containing the 16 octets of a Zenoh ID. */ typedef _z_id_t z_id_t; /** * Represents an ID globally identifying an entity in a Zenoh system. */ typedef _z_entity_global_id_t z_entity_global_id_t; /* * Represents timestamp value in Zenoh */ typedef _z_timestamp_t z_timestamp_t; /** * Represents an array of bytes. */ _Z_OWNED_TYPE_VALUE(_z_slice_t, slice) _Z_VIEW_TYPE(_z_slice_t, slice) /** * Represents a container for slices. */ _Z_OWNED_TYPE_VALUE(_z_bytes_t, bytes) /** * Represents a writer for data. */ _Z_OWNED_TYPE_VALUE(_z_bytes_writer_t, bytes_writer) /** * A reader for data. */ typedef _z_bytes_reader_t z_bytes_reader_t; /** * An iterator over slices of data. */ typedef struct { const _z_bytes_t *_bytes; size_t _slice_idx; } z_bytes_slice_iterator_t; /** * Represents a string without null-terminator. */ _Z_OWNED_TYPE_VALUE(_z_string_t, string) _Z_VIEW_TYPE(_z_string_t, string) /** * Represents a key expression in Zenoh. */ _Z_OWNED_TYPE_VALUE(_z_declared_keyexpr_t, keyexpr) _Z_VIEW_TYPE(_z_declared_keyexpr_t, keyexpr) /** * Represents a Zenoh configuration, used to configure Zenoh sessions upon opening. */ _Z_OWNED_TYPE_VALUE(_z_config_t, config) /** * Represents a Zenoh Session. */ _Z_OWNED_TYPE_RC(_z_session_rc_t, session) /** * Represents a Zenoh Subscriber entity. */ _Z_OWNED_TYPE_VALUE(_z_subscriber_t, subscriber) /** * Represents a Zenoh Publisher entity. */ _Z_OWNED_TYPE_VALUE(_z_publisher_t, publisher) /** * Represents a Zenoh Querier entity. */ _Z_OWNED_TYPE_VALUE(_z_querier_t, querier) /** * Represents a Zenoh Matching listener entity. */ _Z_OWNED_TYPE_VALUE(_z_matching_listener_t, matching_listener) /** * Represents a Zenoh Queryable entity. */ _Z_OWNED_TYPE_VALUE(_z_queryable_t, queryable) /** * Represents a Zenoh Query entity, received by Zenoh Queryable entities. */ _Z_OWNED_TYPE_RC(_z_query_rc_t, query) /** * Represents the encoding of a payload, in a MIME-like format. */ _Z_OWNED_TYPE_VALUE(_z_encoding_t, encoding) /** * Represents a Zenoh reply error. */ _Z_OWNED_TYPE_VALUE(_z_value_t, reply_err) /** * Represents sample source information. */ typedef _z_source_info_t z_source_info_t; /** * A struct that indicates if there exist Subscribers matching the Publisher's key expression or Queryables matching * Querier's key expression and target. * Members: * bool matching: true if there exist matching Zenoh entities, false otherwise. */ typedef _z_matching_status_t z_matching_status_t; /** * Represents the configuration used to configure a subscriber upon declaration :c:func:`z_declare_subscriber`. */ typedef struct { #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 z_locality_t allowed_origin; #else uint8_t __dummy; // keep struct non-empty when locality is disabled #endif } z_subscriber_options_t; /** * Represents the configuration used to configure a zenoh session upon opening :c:func:`z_open`. * * Members: * bool auto_start_read_task: Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are * now started automatically when session is created. * bool auto_start_lease_task: Deprecated, if Z_FEATURE_MULTI_THREAD is enabled background tasks are now started * automatically when session is created. * z_task_attr_t* executor_task_attributes: the attributes to pass to zenoh session executor thread running read, * lease, keep alive, join, connect and other tasks in the background (only when Z_FEATURE_MULTI_THREAD is enabled). * bool auto_start_admin_space: auto-start admin space after ``z_open()`` (default * false; only when admin space feature is enabled). */ typedef struct { #if Z_FEATURE_MULTI_THREAD == 1 bool auto_start_read_task; bool auto_start_lease_task; z_task_attr_t *executor_task_attributes; #endif #if (Z_FEATURE_ADMIN_SPACE == 1) bool auto_start_admin_space; #endif #if Z_FEATURE_ADMIN_SPACE == 0 && (Z_FEATURE_MULTI_THREAD == 0) uint8_t __dummy; // avoid empty struct #endif } z_open_options_t; /** * Represents the configuration used to configure a zenoh upon closing :c:func:`z_close`. */ typedef struct { uint8_t __dummy; // Just to avoid empty structures that might cause undefined behavior } z_close_options_t; /** * Represents the reply consolidation mode to apply on replies to a :c:func:`z_get`. * * Members: * z_consolidation_mode_t mode: the consolidation mode, see :c:type:`z_consolidation_mode_t` */ typedef struct { z_consolidation_mode_t mode; } z_query_consolidation_t; /** * A cancellation token used to interrupt get requests (unstable). * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ _Z_OWNED_TYPE_RC(_z_cancellation_token_rc_t, cancellation_token) /** * Represents the configuration used to configure a publisher upon declaration with :c:func:`z_declare_publisher`. * * Members: * z_moved_encoding_t *encoding: Default encoding for messages put by this publisher. * z_congestion_control_t congestion_control: The congestion control to apply when routing messages from this * publisher. * z_priority_t priority: The priority of messages issued by this publisher. * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_reliability_t reliability: The reliability that should be used to transmit the data (unstable). */ typedef struct { z_moved_encoding_t *encoding; z_congestion_control_t congestion_control; z_priority_t priority; bool is_express; #ifdef Z_FEATURE_UNSTABLE_API z_reliability_t reliability; #endif #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 z_locality_t allowed_destination; #endif } z_publisher_options_t; /** * Options passed to the :c:func:`z_declare_querier()` function. * * Members: * z_moved_encoding_t *encoding: Default encoding for values sent by this querier. * z_query_target_t target: The Queryables that should be target of the querier queries. * z_query_consolidation_t consolidation: The replies consolidation strategy to apply on replies to the querier * queries. * z_congestion_control_t congestion_control: The congestion control to apply when routing the querier queries. * bool is_express: If set to ``true``, the querier queries will not be batched. This usually has a positive impact on * latency but negative impact on throughput. * z_priority_t priority: The priority of the querier queries. * uint64_t timeout_ms: The timeout for the querier queries in milliseconds. 0 corresponds to default get request * timeout. * z_reply_keyexpr_t accept_replies: The accepted replies for the querier queries. */ typedef struct z_querier_options_t { z_moved_encoding_t *encoding; z_query_target_t target; z_query_consolidation_t consolidation; z_congestion_control_t congestion_control; bool is_express; #if Z_FEATURE_LOCAL_QUERYABLE == 1 z_locality_t allowed_destination; #endif z_priority_t priority; uint64_t timeout_ms; z_reply_keyexpr_t accept_replies; } z_querier_options_t; /** * Options passed to the :c:func:`z_querier_get()` function. * * Members: * z_moved_bytes_t *payload: An optional payload to attach to the query. * z_moved_encoding_t *encoding: An optional encoding of the query payload and or attachment. * z_moved_bytes_t *attachment: An optional attachment to attach to the query. * z_source_info_t* source_info: The source info for the request (unstable). * z_moved_cancellation_token_t *cancellation_token: Token to allow cancelling get operation (unstable). */ typedef struct z_querier_get_options_t { z_moved_bytes_t *payload; z_moved_encoding_t *encoding; z_moved_bytes_t *attachment; #ifdef Z_FEATURE_UNSTABLE_API z_moved_cancellation_token_t *cancellation_token; z_source_info_t *source_info; #endif } z_querier_get_options_t; /** * Represents the configuration used to configure a queryable upon declaration :c:func:`z_declare_queryable`. * * Members: * bool complete: The completeness of the queryable. */ typedef struct { bool complete; #if Z_FEATURE_LOCAL_QUERYABLE == 1 z_locality_t allowed_origin; #endif } z_queryable_options_t; /** * Represents the configuration used to configure a query reply sent via :c:func:`z_query_reply`. * * Members: * z_moved_encoding_t* encoding: The encoding of the payload. * z_congestion_control_t congestion_control: **Deprecated**. This field is ignored. * Congestion control for replies is taken from the query. * z_priority_t priority: **Deprecated**. This field is ignored. * Priority for replies is taken from the query. * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_moved_bytes_t* attachment: An optional attachment to the response. * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_moved_encoding_t *encoding; z_congestion_control_t congestion_control; // Deprecated: ignored, taken from query z_priority_t priority; // Deprecated: ignored, taken from query z_timestamp_t *timestamp; bool is_express; z_moved_bytes_t *attachment; #ifdef Z_FEATURE_UNSTABLE_API z_source_info_t *source_info; #endif } z_query_reply_options_t; /** * Represents the configuration used to configure a query reply delete sent via :c:func:`z_query_reply_del`. * * Members: * z_congestion_control_t congestion_control: **Deprecated**. This field is ignored. * Congestion control for replies is taken from the query. * z_priority_t priority: **Deprecated**. This field is ignored. * Priority for replies is taken from the query. * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_moved_bytes_t* attachment: An optional attachment to the response. * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_congestion_control_t congestion_control; // Deprecated: ignored, taken from query z_priority_t priority; // Deprecated: ignored, taken from query z_timestamp_t *timestamp; bool is_express; z_moved_bytes_t *attachment; #ifdef Z_FEATURE_UNSTABLE_API z_source_info_t *source_info; #endif } z_query_reply_del_options_t; /** * Represents the configuration used to configure a query reply error sent via :c:func:`z_query_reply_err`. * * Members: * z_moved_encoding_t* encoding: The encoding of the payload. */ typedef struct { z_moved_encoding_t *encoding; } z_query_reply_err_options_t; /** * Represents the configuration used to configure a put operation sent via :c:func:`z_put`. * * Members: * z_moved_encoding_t* encoding: The encoding of the payload. * z_congestion_control_t congestion_control: The congestion control to apply when routing this message. * z_priority_t priority: The priority of this message when routed. * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_moved_bytes_t* attachment: An optional attachment to the publication. * z_reliability_t reliability: The reliability that should be used to transmit the data (unstable). * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_moved_encoding_t *encoding; z_congestion_control_t congestion_control; z_priority_t priority; z_timestamp_t *timestamp; bool is_express; z_moved_bytes_t *attachment; #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 z_locality_t allowed_destination; #endif #ifdef Z_FEATURE_UNSTABLE_API z_reliability_t reliability; z_source_info_t *source_info; #endif } z_put_options_t; /** * Represents the configuration used to configure a delete operation sent via :c:func:`z_delete`. * * Members: * z_congestion_control_t congestion_control: The congestion control to apply when routing this message. * z_priority_t priority: The priority of this message when router. * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * z_reliability_t reliability: The reliability that should be used to transmit the data (unstable). * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_congestion_control_t congestion_control; z_priority_t priority; bool is_express; z_timestamp_t *timestamp; #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 z_locality_t allowed_destination; #endif #ifdef Z_FEATURE_UNSTABLE_API z_reliability_t reliability; z_source_info_t *source_info; #endif } z_delete_options_t; /** * Represents the configuration used to configure a put operation by a previously declared publisher, * sent via :c:func:`z_publisher_put`. * * Members: * z_moved_encoding_t* encoding: The encoding of the payload. * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * z_moved_bytes_t* attachment: An optional attachment to the publication. * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_moved_encoding_t *encoding; z_timestamp_t *timestamp; z_moved_bytes_t *attachment; #ifdef Z_FEATURE_UNSTABLE_API z_source_info_t *source_info; #endif } z_publisher_put_options_t; /** * Represents the configuration used to configure a delete operation by a previously declared publisher, * sent via :c:func:`z_publisher_delete`. * * Members: * z_timestamp_t *timestamp: The API level timestamp (e.g. of the data when it was created). * z_source_info_t* source_info: The source info for the message (unstable). */ typedef struct { z_timestamp_t *timestamp; #ifdef Z_FEATURE_UNSTABLE_API z_source_info_t *source_info; #endif } z_publisher_delete_options_t; /** * Represents the configuration used to configure a get operation sent via :c:func:`z_get`. * * Members: * z_moved_bytes_t* payload: The payload to include in the query. * z_moved_encoding_t* encoding: Payload encoding. * z_query_consolidation_t consolidation: The replies consolidation strategy to apply on replies. * z_congestion_control_t congestion_control: The congestion control to apply when routing the query. * z_priority_t priority: The priority of the query. * bool is_express: If ``true``, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * z_query_target_t target: The queryables that should be targeted by this get. * uint64_t timeout_ms: Query timeout in milliseconds. 0 means default timeout. 0 corresponds to default get request * timeout. * z_moved_bytes_t* attachment: An optional attachment to the query. * z_source_info_t* source_info: The source info for the request (unstable). * z_moved_cancellation_token_t *cancellation_token: Token to allow cancelling get operation (unstable). * z_reply_keyexpr_t accept_replies: The type of accepted replies for the query. */ typedef struct { z_moved_bytes_t *payload; z_moved_encoding_t *encoding; z_query_consolidation_t consolidation; z_congestion_control_t congestion_control; z_priority_t priority; bool is_express; #if Z_FEATURE_LOCAL_QUERYABLE == 1 z_locality_t allowed_destination; #endif z_query_target_t target; uint64_t timeout_ms; z_moved_bytes_t *attachment; #ifdef Z_FEATURE_UNSTABLE_API z_source_info_t *source_info; z_moved_cancellation_token_t *cancellation_token; #endif z_reply_keyexpr_t accept_replies; } z_get_options_t; #if Z_FEATURE_MULTI_THREAD == 1 || defined(SPHINX_DOCS) /** * Represents the configuration used to configure a read task started via :c:func:`zp_start_read_task`. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. */ typedef struct { z_task_attr_t *task_attributes; } zp_task_read_options_t; /** * Represents the configuration used to configure a lease task started via :c:func:`zp_start_lease_task`. * * Note: only if Z_FEATURE_MULTI_THREAD is enabled. */ typedef struct { z_task_attr_t *task_attributes; } zp_task_lease_options_t; #endif #if Z_FEATURE_MULTI_THREAD == 0 || defined(SPHINX_DOCS) /** * Represents the configuration used to configure a read operation started via :c:func:`zp_read`. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. */ typedef struct { bool single_read; // Read a single packet instead of the whole buffer } zp_read_options_t; /** * Represents the configuration used to configure a send keep alive operation started via :c:func:`zp_send_keep_alive`. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. */ typedef struct { uint8_t __dummy; // Just to avoid empty structures that might cause undefined behavior } zp_send_keep_alive_options_t; /** * Represents the configuration used to configure a send join operation started via :c:func:`zp_send_join`. * * Note: only if Z_FEATURE_MULTI_THREAD is disabled. */ typedef struct { uint8_t __dummy; // Just to avoid empty structures that might cause undefined behavior } zp_send_join_options_t; #endif /** * Represents the configuration used to configure a publisher upon declaration with :c:func:`z_declare_publisher`. * * Members: * uint64_t timeout_ms: The maximum duration in ms the scouting can take. * z_what_t what: Type of entities to scout for. */ typedef struct { uint32_t timeout_ms; z_what_t what; } z_scout_options_t; /** * Represents missed samples. * * Members: * source: The source of missed samples. * nb: The number of missed samples. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ typedef struct { z_entity_global_id_t source; uint32_t nb; } ze_miss_t; /** * Represents a data sample. */ _Z_OWNED_TYPE_VALUE(_z_sample_t, sample) /** * Represents the content of a ``hello`` message returned by a zenoh entity as a reply to a `scout` message. */ _Z_OWNED_TYPE_VALUE(_z_hello_t, hello) /** * Represents the reply to a query. */ _Z_OWNED_TYPE_VALUE(_z_reply_t, reply) /** * Represents an array of non null-terminated string. */ _Z_OWNED_TYPE_VALUE(_z_string_svec_t, string_array) #if Z_FEATURE_CONNECTIVITY == 1 /** * Represents a transport connected to the current session. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_info_transport_t { z_id_t _zid; z_whatami_t _whatami; bool _is_qos; bool _is_multicast; bool _is_shm; } _z_info_transport_t; _Z_OWNED_TYPE_VALUE(_z_info_transport_t, transport) /** * Represents a link connected to the current session. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_info_link_t { z_id_t _zid; _z_string_t _src; _z_string_t _dst; uint16_t _mtu; bool _is_streamed; bool _is_reliable; } _z_info_link_t; _Z_OWNED_TYPE_VALUE(_z_info_link_t, link) /** * Represents a transport connectivity event. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_info_transport_event_t { z_sample_kind_t kind; _z_info_transport_t transport; } _z_info_transport_event_t; _Z_OWNED_TYPE_VALUE(_z_info_transport_event_t, transport_event) /** * Represents a link connectivity event. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_info_link_event_t { z_sample_kind_t kind; _z_info_link_t link; } _z_info_link_event_t; _Z_OWNED_TYPE_VALUE(_z_info_link_event_t, link_event) /** * Represents a transport events listener entity. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_transport_events_listener_t { size_t _id; _z_session_weak_t _session; _z_sync_group_t _callback_drop_sync_group; } _z_transport_events_listener_t; _Z_OWNED_TYPE_VALUE(_z_transport_events_listener_t, transport_events_listener) /** * Represents a link events listener entity. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct _z_link_events_listener_t { size_t _id; _z_session_weak_t _session; _z_sync_group_t _callback_drop_sync_group; } _z_link_events_listener_t; _Z_OWNED_TYPE_VALUE(_z_link_events_listener_t, link_events_listener) /** * Options passed to the :c:func:`z_declare_transport_events_listener()` function. * * Members: * bool history: If set, the listener receives current transports as ``PUT`` events before future events. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct z_transport_events_listener_options_t { bool history; } z_transport_events_listener_options_t; /** * Options passed to the :c:func:`z_declare_link_events_listener()` function. * * Members: * bool history: If set, the listener receives current links as ``PUT`` events before future events. * z_moved_transport_t *transport: Optional transport filter. If set, only events for this transport are delivered. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct z_link_events_listener_options_t { bool history; z_moved_transport_t *transport; } z_link_events_listener_options_t; /** * Options passed to the :c:func:`z_info_links()` function. * * Members: * z_moved_transport_t *transport: Optional transport filter. If set, only links for this transport are returned. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ typedef struct z_info_links_options_t { z_moved_transport_t *transport; } z_info_links_options_t; #endif typedef _z_drop_handler_t z_closure_drop_callback_t; typedef _z_closure_sample_callback_t z_closure_sample_callback_t; typedef struct { void *context; z_closure_sample_callback_t call; z_closure_drop_callback_t drop; } _z_closure_sample_t; /** * Represents the sample closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_sample_t, closure_sample) typedef _z_closure_query_callback_t z_closure_query_callback_t; typedef struct { void *context; z_closure_query_callback_t call; z_closure_drop_callback_t drop; } _z_closure_query_t; /** * Represents the query callback closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_query_t, closure_query) typedef _z_closure_reply_callback_t z_closure_reply_callback_t; typedef struct { void *context; z_closure_reply_callback_t call; z_closure_drop_callback_t drop; } _z_closure_reply_t; /** * Represents the query reply callback closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_reply_t, closure_reply) typedef void (*z_closure_hello_callback_t)(z_loaned_hello_t *hello, void *arg); typedef struct { void *context; z_closure_hello_callback_t call; z_closure_drop_callback_t drop; } _z_closure_hello_t; /** * Represents the Zenoh ID callback closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_hello_t, closure_hello) typedef void (*z_closure_zid_callback_t)(const z_id_t *id, void *arg); typedef struct { void *context; z_closure_zid_callback_t call; z_closure_drop_callback_t drop; } _z_closure_zid_t; /** * Represents the Zenoh ID callback closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_zid_t, closure_zid) #if Z_FEATURE_CONNECTIVITY == 1 typedef void (*z_closure_transport_callback_t)(z_loaned_transport_t *transport, void *arg); typedef struct { void *context; z_closure_transport_callback_t call; z_closure_drop_callback_t drop; } _z_closure_transport_t; /** * Represents the transport callback closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ _Z_OWNED_TYPE_VALUE(_z_closure_transport_t, closure_transport) typedef void (*z_closure_link_callback_t)(z_loaned_link_t *link, void *arg); typedef struct { void *context; z_closure_link_callback_t call; z_closure_drop_callback_t drop; } _z_closure_link_t; /** * Represents the link callback closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ _Z_OWNED_TYPE_VALUE(_z_closure_link_t, closure_link) typedef void (*z_closure_transport_event_callback_t)(z_loaned_transport_event_t *event, void *arg); typedef struct { void *context; z_closure_transport_event_callback_t call; z_closure_drop_callback_t drop; } _z_closure_transport_event_t; /** * Represents the transport event callback closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ _Z_OWNED_TYPE_VALUE(_z_closure_transport_event_t, closure_transport_event) typedef void (*z_closure_link_event_callback_t)(z_loaned_link_event_t *event, void *arg); typedef struct { void *context; z_closure_link_event_callback_t call; z_closure_drop_callback_t drop; } _z_closure_link_event_t; /** * Represents the link event callback closure. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future * release. */ _Z_OWNED_TYPE_VALUE(_z_closure_link_event_t, closure_link_event) #endif typedef _z_closure_matching_status_callback_t z_closure_matching_status_callback_t; typedef _z_closure_matching_status_t z_closure_matching_status_t; /** * Represents the matching status callback closure. */ _Z_OWNED_TYPE_VALUE(_z_closure_matching_status_t, closure_matching_status) typedef void (*ze_closure_miss_callback_t)(const ze_miss_t *miss, void *arg); typedef struct { void *context; ze_closure_miss_callback_t call; z_closure_drop_callback_t drop; } _ze_closure_miss_t; /** * Represents the sample miss callback closure. */ _Z_OWNED_TYPE_VALUE_PREFIX(ze, _ze_closure_miss_t, closure_miss) #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_API_TYPES_H */ ================================================ FILE: include/zenoh-pico/collections/advanced_cache.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_COLLECTIONS_ADVANCED_CACHE_H #define INCLUDE_ZENOH_PICO_COLLECTIONS_ADVANCED_CACHE_H #include #include #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/ring.h" #ifdef __cplusplus extern "C" { #endif /** * Represents the set of options that can be applied to an advaned publishers cache. * The cache allows advanced subscribers to recover history and/or lost samples. * * Members: * bool is_enabled: Must be set to ``true``, to enable the cache. * size_t max_samples: Number of samples to keep for each resource. * z_congestion_control_t congestion_control: The congestion control to apply to replies. * z_priority_t priority: The priority of replies. * bool is_express: If set to ``true``, this cache replies will not be batched. This usually * has a positive impact on latency but negative impact on throughput. */ typedef struct { bool is_enabled; size_t max_samples; z_congestion_control_t congestion_control; z_priority_t priority; bool is_express; bool _liveliness; // TODO: Private as not yet exposed in Zenoh implementation. } ze_advanced_publisher_cache_options_t; typedef struct { _z_sample_ring_t _cache; _z_sample_t *_outbox; size_t _outbox_cap; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex; _z_mutex_t _outbox_mutex; #endif z_owned_queryable_t _queryable; z_owned_liveliness_token_t _liveliness; z_congestion_control_t _congestion_control; z_priority_t _priority; bool _is_express; } _ze_advanced_cache_t; #if Z_FEATURE_ADVANCED_PUBLICATION == 1 _ze_advanced_cache_t *_ze_advanced_cache_new(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const z_loaned_keyexpr_t *suffix, const ze_advanced_publisher_cache_options_t options); z_result_t _ze_advanced_cache_add(_ze_advanced_cache_t *cache, _z_sample_t *sample); void _ze_advanced_cache_free(_ze_advanced_cache_t **xs); #endif #ifdef __cplusplus } #endif #endif // INCLUDE_ZENOH_PICO_COLLECTIONS_ADVANCED_CACHE_H ================================================ FILE: include/zenoh-pico/collections/arc_slice.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_ARC_SLICE_H #define ZENOH_PICO_COLLECTIONS_ARC_SLICE_H #include #include #include #include #include #include "refcount.h" #include "slice.h" #include "zenoh-pico/system/common/platform.h" #ifdef __cplusplus extern "C" { #endif _Z_SIMPLE_REFCOUNT_DEFINE(_z_slice, _z_slice) /*-------- ArcSlice --------*/ /** * An atomically reference counted subslice. * * Members: * _z_slice_simple_rc_t len: Rc counted slice. * size_t start: Offset to the subslice start. * size_t len: Length of the subslice. */ typedef struct { _z_slice_simple_rc_t slice; size_t start; size_t len; } _z_arc_slice_t; static inline _z_arc_slice_t _z_arc_slice_empty(void) { return (_z_arc_slice_t){0}; } static inline size_t _z_arc_slice_len(const _z_arc_slice_t* s) { return s->len; } static inline bool _z_arc_slice_is_empty(const _z_arc_slice_t* s) { return _z_arc_slice_len(s) == 0; } static inline _z_arc_slice_t _z_arc_slice_wrap_slice_rc(_z_slice_simple_rc_t* slice_rc, size_t offset, size_t len) { assert(offset + len <= _z_slice_simple_rc_value(slice_rc)->len); _z_arc_slice_t arc_s; arc_s.slice = _z_slice_simple_rc_clone(slice_rc); arc_s.len = len; arc_s.start = offset; return arc_s; } static inline const uint8_t* _z_arc_slice_data(const _z_arc_slice_t* s) { return _z_slice_simple_rc_value(&s->slice)->start + s->start; } static inline z_result_t _z_arc_slice_drop(_z_arc_slice_t* s) { _z_slice_simple_rc_drop(&s->slice); return _Z_RES_OK; } static inline size_t _z_arc_slice_size(const _z_arc_slice_t* s) { (void)s; return sizeof(_z_arc_slice_t); } _z_arc_slice_t _z_arc_slice_wrap(_z_slice_t* s, size_t offset, size_t len); _z_arc_slice_t _z_arc_slice_get_subslice(const _z_arc_slice_t* s, size_t offset, size_t len); z_result_t _z_arc_slice_copy(_z_arc_slice_t* dst, const _z_arc_slice_t* src); z_result_t _z_arc_slice_move(_z_arc_slice_t* dst, _z_arc_slice_t* src); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_ARC_SLICE_H */ ================================================ FILE: include/zenoh-pico/collections/array.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_ARRAY_H #define ZENOH_PICO_COLLECTIONS_ARRAY_H #include #include #include #ifdef __cplusplus extern "C" { #endif /*------------------ Internal Array Macros ------------------*/ #define _Z_ARRAY_DEFINE(name, type) \ typedef struct { \ size_t _len; \ type *_val; \ } name##_array_t; \ static inline name##_array_t name##_array_empty(void) { \ name##_array_t a; \ a._val = NULL; \ a._len = 0; \ return a; \ } \ static inline name##_array_t name##_array_make(size_t capacity) { \ name##_array_t a; \ a._val = (type *)z_malloc(capacity * sizeof(type)); \ if (a._val != NULL) { \ a._len = capacity; \ } else { \ a._len = 0; \ } \ return a; \ } \ static inline void name##_array_move(name##_array_t *dst, name##_array_t *src) { \ dst->_len = src->_len; \ dst->_val = src->_val; \ src->_len = 0; \ src->_val = NULL; \ } \ static inline void name##_array_copy(name##_array_t *dst, name##_array_t *src) { \ dst->_len = src->_len; \ memcpy(&dst->_val, &src->_val, src->_len); \ } \ static inline type *name##_array_get(const name##_array_t *a, size_t k) { return &a->_val[k]; } \ static inline size_t name##_array_len(const name##_array_t *a) { return a->_len; } \ static inline bool name##_array_is_empty(const name##_array_t *a) { return a->_len == 0; } \ static inline void name##_array_clear(name##_array_t *a) { \ for (size_t i = 0; i < a->_len; i++) { \ name##_elem_clear(&a->_val[i]); \ } \ z_free(a->_val); \ a->_len = 0; \ a->_val = NULL; \ } \ static inline void name##_array_free(name##_array_t **a) { \ name##_array_t *ptr = *a; \ if (ptr != NULL) { \ name##_array_clear(ptr); \ z_free(ptr); \ *a = NULL; \ } \ } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_ARRAY_H */ ================================================ FILE: include/zenoh-pico/collections/atomic.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_ATOMIC_H #define ZENOH_PICO_COLLECTIONS_ATOMIC_H #include #include typedef struct { size_t _value; } _z_atomic_size_t; typedef enum { _z_memory_order_relaxed, _z_memory_order_acquire, _z_memory_order_release, _z_memory_order_acq_rel, _z_memory_order_seq_cst } _z_memory_order_t; void _z_atomic_size_init(_z_atomic_size_t *var, size_t value); size_t _z_atomic_size_load(_z_atomic_size_t *var, _z_memory_order_t order); void _z_atomic_size_store(_z_atomic_size_t *var, size_t val, _z_memory_order_t order); size_t _z_atomic_size_fetch_add(_z_atomic_size_t *var, size_t val, _z_memory_order_t order); size_t _z_atomic_size_fetch_sub(_z_atomic_size_t *var, size_t val, _z_memory_order_t order); bool _z_atomic_size_compare_exchange_strong(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure); bool _z_atomic_size_compare_exchange_weak(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure); void _z_atomic_thread_fence(_z_memory_order_t order); typedef _z_atomic_size_t _z_atomic_bool_t; static inline void _z_atomic_bool_init(_z_atomic_bool_t *var, bool value) { _z_atomic_size_init(var, value ? 1 : 0); } static inline bool _z_atomic_bool_load(_z_atomic_bool_t *var, _z_memory_order_t order) { return _z_atomic_size_load(var, order) != 0; } static inline void _z_atomic_bool_store(_z_atomic_bool_t *var, bool val, _z_memory_order_t order) { _z_atomic_size_store(var, val ? 1 : 0, order); } static inline bool _z_atomic_bool_compare_exchange_strong(_z_atomic_bool_t *var, bool *expected, bool desired, _z_memory_order_t success, _z_memory_order_t failure) { size_t expected_val = *expected ? 1 : 0; bool result = _z_atomic_size_compare_exchange_strong(var, &expected_val, desired ? 1 : 0, success, failure); *expected = expected_val != 0; return result; } static inline bool _z_atomic_bool_compare_exchange_weak(_z_atomic_bool_t *var, bool *expected, bool desired, _z_memory_order_t success, _z_memory_order_t failure) { size_t expected_val = *expected ? 1 : 0; bool result = _z_atomic_size_compare_exchange_weak(var, &expected_val, desired ? 1 : 0, success, failure); *expected = expected_val != 0; return result; } #endif ================================================ FILE: include/zenoh-pico/collections/bytes.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_BYTES_H #define ZENOH_PICO_COLLECTIONS_BYTES_H #include #include #include #include "arc_slice.h" #include "vec.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif _Z_ELEM_DEFINE(_z_arc_slice, _z_arc_slice_t, _z_arc_slice_size, _z_arc_slice_drop, _z_arc_slice_copy, _z_arc_slice_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SVEC_DEFINE(_z_arc_slice, _z_arc_slice_t) /*-------- Bytes --------*/ /** * A container for slices. * * Members: * _z_slice_vec_t _slices: contents of the container. */ typedef struct { _z_arc_slice_svec_t _slices; } _z_bytes_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_bytes_t _z_bytes_null(void) { return (_z_bytes_t){0}; } static inline void _z_bytes_alias_arc_slice(_z_bytes_t *dst, _z_arc_slice_t *s) { dst->_slices = _z_arc_slice_svec_alias_element(s); } static inline _z_bytes_t _z_bytes_alias(const _z_bytes_t *src) { if (src == NULL) { return _z_bytes_null(); } _z_bytes_t dst; dst._slices = src->_slices; return dst; } static inline _z_bytes_t _z_bytes_steal(_z_bytes_t *src) { _z_bytes_t b = *src; src->_slices._len = 0; src->_slices._val = NULL; return b; } static inline size_t _z_bytes_num_slices(const _z_bytes_t *bs) { return _z_arc_slice_svec_len(&bs->_slices); } static inline _z_arc_slice_t *_z_bytes_get_slice(const _z_bytes_t *bs, size_t i) { if (i >= _z_bytes_num_slices(bs)) return NULL; return _z_arc_slice_svec_get(&bs->_slices, i); } static inline void _z_bytes_drop(_z_bytes_t *bytes) { _z_arc_slice_svec_clear(&bytes->_slices); } bool _z_bytes_check(const _z_bytes_t *bytes); z_result_t _z_bytes_append_bytes(_z_bytes_t *dst, _z_bytes_t *src); z_result_t _z_bytes_append_slice(_z_bytes_t *dst, _z_arc_slice_t *s); z_result_t _z_bytes_copy(_z_bytes_t *dst, const _z_bytes_t *src); _z_bytes_t _z_bytes_duplicate(const _z_bytes_t *src); z_result_t _z_bytes_move(_z_bytes_t *dst, _z_bytes_t *src); void _z_bytes_free(_z_bytes_t **bs); size_t _z_bytes_len(const _z_bytes_t *bs); bool _z_bytes_is_empty(const _z_bytes_t *bs); z_result_t _z_bytes_to_slice(const _z_bytes_t *bytes, _z_slice_t *s); z_result_t _z_bytes_from_slice(_z_bytes_t *b, _z_slice_t *s); size_t _z_bytes_to_buf(const _z_bytes_t *bytes, uint8_t *dst, size_t len); z_result_t _z_bytes_from_buf(_z_bytes_t *b, const uint8_t *src, size_t len); _z_slice_t _z_bytes_try_get_contiguous(const _z_bytes_t *bs); typedef struct { size_t slice_idx; size_t in_slice_idx; size_t byte_idx; const _z_bytes_t *bytes; } _z_bytes_reader_t; _z_bytes_reader_t _z_bytes_get_reader(const _z_bytes_t *bytes); z_result_t _z_bytes_reader_seek(_z_bytes_reader_t *reader, int64_t offset, int origin); int64_t _z_bytes_reader_tell(const _z_bytes_reader_t *reader); z_result_t _z_bytes_reader_read_slices(_z_bytes_reader_t *reader, size_t len, _z_bytes_t *out); size_t _z_bytes_reader_read(_z_bytes_reader_t *reader, uint8_t *buf, size_t len); typedef struct { _z_arc_slice_t *cache; _z_bytes_t bytes; } _z_bytes_writer_t; bool _z_bytes_writer_is_empty(const _z_bytes_writer_t *writer); bool _z_bytes_writer_check(const _z_bytes_writer_t *writer); _z_bytes_writer_t _z_bytes_writer_from_bytes(_z_bytes_t *bytes); _z_bytes_writer_t _z_bytes_writer_empty(void); z_result_t _z_bytes_writer_write_all(_z_bytes_writer_t *writer, const uint8_t *src, size_t len); z_result_t _z_bytes_writer_append_z_bytes(_z_bytes_writer_t *writer, _z_bytes_t *bytes); z_result_t _z_bytes_writer_append_slice(_z_bytes_writer_t *writer, _z_arc_slice_t *bytes); _z_bytes_t _z_bytes_writer_finish(_z_bytes_writer_t *writer); void _z_bytes_writer_clear(_z_bytes_writer_t *writer); z_result_t _z_bytes_writer_move(_z_bytes_writer_t *dst, _z_bytes_writer_t *src); size_t _z_bytes_reader_remaining(const _z_bytes_reader_t *reader); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_BYTES_H */ ================================================ FILE: include/zenoh-pico/collections/cat.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_CAT_H #define ZENOH_PICO_COLLECTIONS_CAT_H #define _ZP_CAT_INNER(a, b) a##_##b #define _ZP_CAT(a, b) _ZP_CAT_INNER(a, b) #endif ================================================ FILE: include/zenoh-pico/collections/deque_template.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // user needs to define the following macros before including this file // _ZP_DEQUE_TEMPLATE_ELEM_TYPE: the type of the elements in the buffer // _ZP_DEQUE_TEMPLATE_NAME: the name of the buffer type to generate (without the _t suffix) // _ZP_DEQUE_TEMPLATE_SIZE: the maximum size of the circular buffer (optional, default is 16) // _ZP_DEQUE_TEMPLATE_ALLOCATOR_TYPE: the type of the allocator to use for the buffer nodes (optional, default is a // simple z_malloc/z_free allocator) _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME: the name of the function to destroy an // element (optional, default is a no-op function) _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME: the name of the function to // move an element (optional, default is an element-wise move function that uses the copy function and then clears the // source element) #include #include #include "zenoh-pico/collections/cat.h" #ifndef _ZP_DEQUE_TEMPLATE_ELEM_TYPE #error "_ZP_DEQUE_TEMPLATE_ELEM_TYPE must be defined before including deque_template.h" #define _ZP_DEQUE_TEMPLATE_ELEM_TYPE int #endif #ifndef _ZP_DEQUE_TEMPLATE_SIZE #define _ZP_DEQUE_TEMPLATE_SIZE 16 #endif #ifndef _ZP_DEQUE_TEMPLATE_NAME #define _ZP_DEQUE_TEMPLATE_NAME _ZP_CAT(_ZP_CAT(_ZP_DEQUE_TEMPLATE_ELEM_TYPE, deque), _ZP_DEQUE_TEMPLATE_SIZE) #endif #ifndef _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME #define _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME(x) (void)(x) #endif #ifndef _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME #define _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME(dst, src) \ *(dst) = *(src); \ _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME(src); #endif #define _ZP_DEQUE_TEMPLATE_TYPE _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, t) typedef struct _ZP_DEQUE_TEMPLATE_TYPE { _ZP_DEQUE_TEMPLATE_ELEM_TYPE _buffer[_ZP_DEQUE_TEMPLATE_SIZE]; size_t _start; size_t _end; } _ZP_DEQUE_TEMPLATE_TYPE; static inline _ZP_DEQUE_TEMPLATE_TYPE _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, new)(void) { _ZP_DEQUE_TEMPLATE_TYPE deque = {0}; return deque; } static inline size_t _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, size)(const _ZP_DEQUE_TEMPLATE_TYPE *deque) { if (deque->_start < deque->_end) { return deque->_end - deque->_start; } else if (deque->_start == 0 && deque->_end == 0) { return 0; } return _ZP_DEQUE_TEMPLATE_SIZE - deque->_start + deque->_end; } static inline void _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, destroy)(_ZP_DEQUE_TEMPLATE_TYPE *deque) { size_t count = _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, size)(deque); for (size_t i = 0; i < count; i++) { size_t idx = deque->_start + i; if (i >= _ZP_DEQUE_TEMPLATE_SIZE) { idx -= _ZP_DEQUE_TEMPLATE_SIZE; } _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME(&deque->_buffer[idx]); } deque->_start = 0; deque->_end = 0; } static inline bool _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, is_empty)(const _ZP_DEQUE_TEMPLATE_TYPE *deque) { return _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, size)(deque) == 0; } static inline bool _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, push_back)(_ZP_DEQUE_TEMPLATE_TYPE *deque, _ZP_DEQUE_TEMPLATE_ELEM_TYPE *elem) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, size)(deque) == _ZP_DEQUE_TEMPLATE_SIZE) { return false; } if (deque->_end == _ZP_DEQUE_TEMPLATE_SIZE) { deque->_end = 0; } _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME(&deque->_buffer[deque->_end], elem); deque->_end++; return true; } static inline bool _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, pop_back)(_ZP_DEQUE_TEMPLATE_TYPE *deque, _ZP_DEQUE_TEMPLATE_ELEM_TYPE *out) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, is_empty)(deque)) { return false; } if (deque->_end == 0) { deque->_end = _ZP_DEQUE_TEMPLATE_SIZE; } deque->_end--; _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME(out, &deque->_buffer[deque->_end]); if (deque->_end == deque->_start) { deque->_end = 0; deque->_start = 0; } return true; } static inline _ZP_DEQUE_TEMPLATE_ELEM_TYPE *_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, back)(_ZP_DEQUE_TEMPLATE_TYPE *deque) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, is_empty)(deque)) { return NULL; } size_t idx = deque->_end == 0 ? _ZP_DEQUE_TEMPLATE_SIZE - 1 : deque->_end - 1; return &deque->_buffer[idx]; } static inline bool _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, push_front)(_ZP_DEQUE_TEMPLATE_TYPE *deque, _ZP_DEQUE_TEMPLATE_ELEM_TYPE *elem) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, size)(deque) == _ZP_DEQUE_TEMPLATE_SIZE) { return false; } if (deque->_start == 0) { deque->_start = _ZP_DEQUE_TEMPLATE_SIZE; } deque->_start--; _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME(&deque->_buffer[deque->_start], elem); return true; } static inline bool _ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, pop_front)(_ZP_DEQUE_TEMPLATE_TYPE *deque, _ZP_DEQUE_TEMPLATE_ELEM_TYPE *out) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, is_empty)(deque)) { return false; } _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME(out, &deque->_buffer[deque->_start]); deque->_start++; if (deque->_end == deque->_start) { deque->_end = 0; deque->_start = 0; } return true; } static inline _ZP_DEQUE_TEMPLATE_ELEM_TYPE *_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, front)(_ZP_DEQUE_TEMPLATE_TYPE *deque) { if (_ZP_CAT(_ZP_DEQUE_TEMPLATE_NAME, is_empty)(deque)) { return NULL; } return &deque->_buffer[deque->_start]; } #undef _ZP_DEQUE_TEMPLATE_TYPE #undef _ZP_DEQUE_TEMPLATE_ELEM_TYPE #undef _ZP_DEQUE_TEMPLATE_NAME #undef _ZP_DEQUE_TEMPLATE_NODE_TYPE #undef _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME #undef _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME #undef _ZP_DEQUE_TEMPLATE_SIZE ================================================ FILE: include/zenoh-pico/collections/element.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_ELEMENT_H #define ZENOH_PICO_COLLECTIONS_ELEMENT_H #include #include #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif /*-------- element functions --------*/ typedef size_t (*z_element_size_f)(void *e); typedef void (*z_element_clear_f)(void *e); typedef void (*z_element_free_f)(void **e); typedef void (*z_element_copy_f)(void *dst, const void *src); typedef void (*z_element_move_f)(void *dst, void *src); typedef void *(*z_element_clone_f)(const void *e); typedef bool (*z_element_eq_f)(const void *left, const void *right); typedef int (*z_element_cmp_f)(const void *left, const void *right); typedef size_t (*z_element_hash_f)(const void *e); #define _Z_ELEM_DEFINE(name, type, elem_size_f, elem_clear_f, elem_copy_f, elem_move_f, elem_eq_f, elem_cmp_f, \ elem_hash_f) \ typedef bool (*name##_eq_f)(const type *left, const type *right); \ typedef int (*name##_cmp_f)(const type *left, const type *right); \ static inline void name##_elem_clear(void *e) { elem_clear_f((type *)e); } \ static inline void name##_elem_free(void **e) { \ type *ptr = (type *)*e; \ if (ptr != NULL) { \ elem_clear_f(ptr); \ z_free(ptr); \ *e = NULL; \ } \ } \ static inline void name##_elem_move(void *dst, void *src) { elem_move_f((type *)dst, (type *)src); } \ static inline void name##_elem_copy(void *dst, const void *src) { elem_copy_f((type *)dst, (type *)src); } \ static inline void *name##_elem_clone(const void *src) { \ type *dst = (type *)z_malloc(elem_size_f((type *)src)); \ if (dst != NULL) { \ elem_copy_f(dst, (type *)src); \ } \ return dst; \ } \ static inline bool name##_elem_eq(const void *left, const void *right) { \ return elem_eq_f((const type *)left, (const type *)right); \ } \ static inline int name##_elem_cmp(const void *left, const void *right) { \ return elem_cmp_f((const type *)left, (const type *)right); \ } \ static inline size_t name##_elem_hash(const void *e) { return elem_hash_f((const type *)e); } /*------------------ void ----------------*/ typedef void _z_noop_t; static inline size_t _z_noop_size(void *s) { (void)(s); return 0; } static inline void _z_noop_clear(void *s) { (void)(s); } static inline void _z_noop_free(void **s) { (void)(s); } static inline void _z_noop_copy(void *dst, const void *src) { _ZP_UNUSED(dst); _ZP_UNUSED(src); } static inline void _z_noop_move(void *dst, void *src) { _ZP_UNUSED(dst); _ZP_UNUSED(src); } static inline bool _z_noop_eq(const void *left, const void *right) { _ZP_UNUSED(left); _ZP_UNUSED(right); return true; } static inline int _z_noop_cmp(const void *left, const void *right) { _ZP_UNUSED(left); _ZP_UNUSED(right); return 0; } static inline size_t _z_noop_hash(const void *e) { _ZP_UNUSED(e); return 0; } _Z_ELEM_DEFINE(_z_noop, _z_noop_t, _z_noop_size, _z_noop_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_ELEMENT_H */ ================================================ FILE: include/zenoh-pico/collections/fifo.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_FIFO_H #define ZENOH_PICO_COLLECTIONS_FIFO_H #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/ring.h" #ifdef __cplusplus extern "C" { #endif /*-------- Fifo Buffer --------*/ typedef struct { _z_ring_t _ring; } _z_fifo_t; z_result_t _z_fifo_init(_z_fifo_t *fifo, size_t capacity); _z_fifo_t _z_fifo_make(size_t capacity); size_t _z_fifo_capacity(const _z_fifo_t *r); size_t _z_fifo_len(const _z_fifo_t *r); bool _z_fifo_is_empty(const _z_fifo_t *r); bool _z_fifo_is_full(const _z_fifo_t *r); void *_z_fifo_push(_z_fifo_t *r, void *e); void _z_fifo_push_drop(_z_fifo_t *r, void *e, z_element_free_f f); void *_z_fifo_pull(_z_fifo_t *r); _z_fifo_t *_z_fifo_clone(const _z_fifo_t *xs, z_element_clone_f d_f); void _z_fifo_clear(_z_fifo_t *v, z_element_free_f f); void _z_fifo_free(_z_fifo_t **xs, z_element_free_f f_f); #define _Z_FIFO_DEFINE(name, type) \ typedef _z_fifo_t name##_fifo_t; \ static inline z_result_t name##_fifo_init(name##_fifo_t *fifo, size_t capacity) { \ return _z_fifo_init(fifo, capacity); \ } \ static inline name##_fifo_t name##_fifo_make(size_t capacity) { return _z_fifo_make(capacity); } \ static inline size_t name##_fifo_capacity(const name##_fifo_t *r) { return _z_fifo_capacity(r); } \ static inline size_t name##_fifo_len(const name##_fifo_t *r) { return _z_fifo_len(r); } \ static inline bool name##_fifo_is_empty(const name##_fifo_t *r) { return _z_fifo_is_empty(r); } \ static inline bool name##_fifo_is_full(const name##_fifo_t *r) { return _z_fifo_is_full(r); } \ static inline type *name##_fifo_push(name##_fifo_t *r, type *e) { return _z_fifo_push(r, (void *)e); } \ static inline void name##_fifo_push_drop(name##_fifo_t *r, type *e) { \ _z_fifo_push_drop(r, (void *)e, name##_elem_free); \ } \ static inline type *name##_fifo_pull(name##_fifo_t *r) { return (type *)_z_fifo_pull(r); } \ static inline void name##_fifo_clear(name##_fifo_t *r) { _z_fifo_clear(r, name##_elem_free); } \ static inline void name##_fifo_free(name##_fifo_t **r) { _z_fifo_free(r, name##_elem_free); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_FIFO_H */ ================================================ FILE: include/zenoh-pico/collections/fifo_mt.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_FIFO_MT_H #define ZENOH_PICO_COLLECTIONS_FIFO_MT_H #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/fifo.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif /*-------- Fifo Buffer Multithreaded --------*/ typedef struct { _z_fifo_t _fifo; bool is_closed; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex; _z_condvar_t _cv_not_full; _z_condvar_t _cv_not_empty; #endif } _z_fifo_mt_t; z_result_t _z_fifo_mt_init(_z_fifo_mt_t *fifo, size_t capacity); _z_fifo_mt_t *_z_fifo_mt_new(size_t capacity); z_result_t _z_fifo_mt_close(_z_fifo_mt_t *fifo); void _z_fifo_mt_clear(_z_fifo_mt_t *fifo, z_element_free_f free_f); void _z_fifo_mt_free(_z_fifo_mt_t *fifo, z_element_free_f free_f); z_result_t _z_fifo_mt_push(const void *src, void *context, z_element_free_f element_free); z_result_t _z_fifo_mt_pull(void *dst, void *context, z_element_move_f element_move); z_result_t _z_fifo_mt_try_pull(void *dst, void *context, z_element_move_f element_move); #ifdef __cplusplus } #endif #endif // ZENOH_PICO_COLLECTIONS_FIFO_MT_H ================================================ FILE: include/zenoh-pico/collections/hashmap.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_HASHMAP_H #define ZENOH_PICO_COLLECTIONS_HASHMAP_H #include #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif #define _Z_DEFAULT_HASHMAP_CAPACITY 16 /** * A hashmap entry with generic keys. * * Members: * void *_key: the key of the entry * void *_val: the value of the entry */ typedef struct { void *_key; void *_val; } _z_hashmap_entry_t; /** * A hashmap with generic keys. * * Members: * size_t _capacity: the number of buckets available in the hashmap * _z_list_t **_vals: the linked list containing the values * z_element_hash_f _f_hash: the hash function used to hash keys * z_element_eq_f _f_equals: the function used to compare keys for equality */ typedef struct { size_t _capacity; _z_list_t **_vals; z_element_hash_f _f_hash; z_element_eq_f _f_equals; } _z_hashmap_t; /** * Iterator for a generic key-value hashmap. */ typedef struct { _z_hashmap_entry_t *_entry; const _z_hashmap_t *_map; size_t _idx; _z_list_t *_list_ptr; } _z_hashmap_iterator_t; void _z_hashmap_init(_z_hashmap_t *map, size_t capacity, z_element_hash_f f_hash, z_element_eq_f f_equals); _z_hashmap_t _z_hashmap_make(size_t capacity, z_element_hash_f f_hash, z_element_eq_f f_equals); void *_z_hashmap_insert(_z_hashmap_t *map, void *key, void *val, z_element_free_f f, bool replace); void *_z_hashmap_get(const _z_hashmap_t *map, const void *key); _z_list_t *_z_hashmap_get_all(const _z_hashmap_t *map, const void *key); void _z_hashmap_remove(_z_hashmap_t *map, const void *key, z_element_free_f f); _z_hashmap_entry_t _z_hashmap_extract(_z_hashmap_t *map, const void *key); size_t _z_hashmap_capacity(const _z_hashmap_t *map); size_t _z_hashmap_len(const _z_hashmap_t *map); bool _z_hashmap_is_empty(const _z_hashmap_t *map); z_result_t _z_hashmap_copy(_z_hashmap_t *dst, const _z_hashmap_t *src, z_element_clone_f f_c); _z_hashmap_t _z_hashmap_clone(const _z_hashmap_t *src, z_element_clone_f f_c, z_element_free_f f_f); void _z_hashmap_clear(_z_hashmap_t *map, z_element_free_f f); void _z_hashmap_free(_z_hashmap_t **map, z_element_free_f f); static inline void _z_hashmap_move(_z_hashmap_t *dst, _z_hashmap_t *src) { *dst = *src; *src = _z_hashmap_make(src->_capacity, src->_f_hash, src->_f_equals); } _z_hashmap_iterator_t _z_hashmap_iterator_make(const _z_hashmap_t *map); bool _z_hashmap_iterator_next(_z_hashmap_iterator_t *iter); void *_z_hashmap_iterator_key(const _z_hashmap_iterator_t *iter); void *_z_hashmap_iterator_value(const _z_hashmap_iterator_t *iter); #define _Z_HASHMAP_DEFINE_INNER(map_name, key_name, val_name, key_type, val_type) \ typedef _z_hashmap_entry_t map_name##_hashmap_entry_t; \ static inline void map_name##_hashmap_entry_elem_free(void **e) { \ map_name##_hashmap_entry_t *ptr = (map_name##_hashmap_entry_t *)*e; \ if (ptr != NULL) { \ key_name##_elem_free(&ptr->_key); \ val_name##_elem_free(&ptr->_val); \ z_free(ptr); \ *e = NULL; \ } \ } \ static inline void *map_name##_hashmap_entry_elem_clone(const void *e) { \ const map_name##_hashmap_entry_t *src = (map_name##_hashmap_entry_t *)e; \ map_name##_hashmap_entry_t *dst = (map_name##_hashmap_entry_t *)z_malloc(sizeof(map_name##_hashmap_entry_t)); \ dst->_key = key_name##_elem_clone(src->_key); \ dst->_val = val_name##_elem_clone(src->_val); \ return dst; \ } \ static inline bool map_name##_hashmap_entry_key_eq(const void *left, const void *right) { \ const map_name##_hashmap_entry_t *l = (const map_name##_hashmap_entry_t *)left; \ const map_name##_hashmap_entry_t *r = (const map_name##_hashmap_entry_t *)right; \ return key_name##_elem_eq(l->_key, r->_key); \ } \ typedef _z_hashmap_t map_name##_hashmap_t; \ typedef _z_hashmap_iterator_t map_name##_hashmap_iterator_t; \ static inline void map_name##_hashmap_init(map_name##_hashmap_t *m) { \ _z_hashmap_init(m, _Z_DEFAULT_HASHMAP_CAPACITY, key_name##_elem_hash, map_name##_hashmap_entry_key_eq); \ } \ static inline map_name##_hashmap_t map_name##_hashmap_make(void) { \ return _z_hashmap_make(_Z_DEFAULT_HASHMAP_CAPACITY, key_name##_elem_hash, map_name##_hashmap_entry_key_eq); \ } \ static inline val_type *map_name##_hashmap_insert(map_name##_hashmap_t *m, key_type *k, val_type *v) { \ return (val_type *)_z_hashmap_insert(m, k, v, map_name##_hashmap_entry_elem_free, true); \ } \ static inline val_type *map_name##_hashmap_insert_push(map_name##_hashmap_t *m, key_type *k, val_type *v) { \ return (val_type *)_z_hashmap_insert(m, k, v, map_name##_hashmap_entry_elem_free, false); \ } \ static inline val_type *map_name##_hashmap_get(const map_name##_hashmap_t *m, const key_type *k) { \ return (val_type *)_z_hashmap_get(m, k); \ } \ static inline _z_list_t *map_name##_hashmap_get_all(const map_name##_hashmap_t *m, const key_type *k) { \ return _z_hashmap_get_all(m, k); \ } \ static inline map_name##_hashmap_t map_name##_hashmap_clone(const map_name##_hashmap_t *m) { \ return _z_hashmap_clone(m, map_name##_hashmap_entry_elem_clone, map_name##_hashmap_entry_elem_free); \ } \ static inline void map_name##_hashmap_remove(map_name##_hashmap_t *m, const key_type *k) { \ _z_hashmap_remove(m, k, map_name##_hashmap_entry_elem_free); \ } \ static inline map_name##_hashmap_entry_t map_name##_hashmap_extract(map_name##_hashmap_t *m, const key_type *k) { \ map_name##_hashmap_entry_t out; \ _z_hashmap_entry_t e = _z_hashmap_extract(m, k); \ out._key = e._key; \ out._val = e._val; \ return out; \ } \ static inline size_t map_name##_hashmap_capacity(map_name##_hashmap_t *m) { return _z_hashmap_capacity(m); } \ static inline size_t map_name##_hashmap_len(map_name##_hashmap_t *m) { return _z_hashmap_len(m); } \ static inline bool map_name##_hashmap_is_empty(const map_name##_hashmap_t *m) { return _z_hashmap_is_empty(m); } \ static inline void map_name##_hashmap_clear(map_name##_hashmap_t *m) { \ _z_hashmap_clear(m, map_name##_hashmap_entry_elem_free); \ } \ static inline void map_name##_hashmap_free(map_name##_hashmap_t **m) { \ _z_hashmap_free(m, map_name##_hashmap_entry_elem_free); \ } \ static inline z_result_t map_name##_hashmap_move(map_name##_hashmap_t *dst, map_name##_hashmap_t *src) { \ _z_hashmap_move(dst, src); \ return _Z_RES_OK; \ } \ static inline map_name##_hashmap_iterator_t map_name##_hashmap_iterator_make(const map_name##_hashmap_t *m) { \ return _z_hashmap_iterator_make(m); \ } \ static inline bool map_name##_hashmap_iterator_next(map_name##_hashmap_iterator_t *iter) { \ return _z_hashmap_iterator_next(iter); \ } \ static inline key_type *map_name##_hashmap_iterator_key(const map_name##_hashmap_iterator_t *iter) { \ return (key_type *)_z_hashmap_iterator_key(iter); \ } \ static inline val_type *map_name##_hashmap_iterator_value(const map_name##_hashmap_iterator_t *iter) { \ return (val_type *)_z_hashmap_iterator_value(iter); \ } #define _Z_HASHMAP_DEFINE(key_name, val_name, key_type, val_type) \ _Z_HASHMAP_DEFINE_INNER(key_name##_##val_name, key_name, val_name, key_type, val_type) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_HASHMAP_H */ ================================================ FILE: include/zenoh-pico/collections/hashmap_template.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // Hashmap with separate chaining using a fixed-size node pool. // // Each bucket is the head of an intrusive singly-linked list. Nodes are // allocated from a flat pool of _ZP_HASHMAP_TEMPLATE_CAPACITY // elements — no heap allocation is performed. // // User must define the following macros before including this file: // // Required: // _ZP_HASHMAP_TEMPLATE_KEY_TYPE // type of the key // _ZP_HASHMAP_TEMPLATE_VAL_TYPE // type of the value // _ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME(key_ptr) -> size_t // hash function for the key // // Optional: // _ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME(key_a_ptr, key_b_ptr) -> bool // equality function for keys (default: memcmp == 0) // _ZP_HASHMAP_TEMPLATE_NAME // base name for all generated symbols // (default: _ZP_CAT(key_type, _ZP_CAT(val_type, hmap))) // _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT // number of hash buckets // (default: 16) // _ZP_HASHMAP_TEMPLATE_CAPACITY // maximum total number of entries that can be stored // (default: _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT) // _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(key_ptr) // destroy a key (default: no-op) // _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(val_ptr) // destroy a value (default: no-op) // _ZP_HASHMAP_TEMPLATE_KEY_MOVE_FN_NAME(dst_ptr, src_ptr) // move a key (default: copy then destroy src) // _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(dst_ptr, src_ptr) // move a value (default: copy then destroy src) #include #include #include #include #include "zenoh-pico/collections/cat.h" // ── Required macros ────────────────────────────────────────────────────────── #ifndef _ZP_HASHMAP_TEMPLATE_KEY_TYPE #error "_ZP_HASHMAP_TEMPLATE_KEY_TYPE must be defined before including hashmap_template.h" #define _ZP_HASHMAP_TEMPLATE_KEY_TYPE int #endif #ifndef _ZP_HASHMAP_TEMPLATE_VAL_TYPE #error "_ZP_HASHMAP_TEMPLATE_VAL_TYPE must be defined before including hashmap_template.h" #define _ZP_HASHMAP_TEMPLATE_VAL_TYPE int #endif #ifndef _ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME #error "_ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME must be defined before including hashmap_template.h" #endif // ── Optional macros with defaults ──────────────────────────────────────────── #ifndef _ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME #define _ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME(key_a_ptr, key_b_ptr) (*(key_a_ptr) == *(key_b_ptr)) #endif #ifndef _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT #define _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT 16 #endif #ifndef _ZP_HASHMAP_TEMPLATE_CAPACITY #define _ZP_HASHMAP_TEMPLATE_CAPACITY _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT #endif #ifndef _ZP_HASHMAP_TEMPLATE_NAME #define _ZP_HASHMAP_TEMPLATE_NAME _ZP_CAT(_ZP_HASHMAP_TEMPLATE_KEY_TYPE, _ZP_CAT(_ZP_HASHMAP_TEMPLATE_VAL_TYPE, hmap)) #endif // ── Index type selection ────────────────────────────────────────────────────── // // Choose the smallest unsigned type whose maximum representable value is // strictly greater than CAPACITY (the extra value is used as the sentinel // "end-of-list / empty-bucket" marker). // // CAPACITY ≤ 254 → uint8_t (sentinel = 255) // CAPACITY ≤ 65534 → uint16_t (sentinel = 65535) // otherwise → uint32_t (sentinel = UINT32_MAX) #if _ZP_HASHMAP_TEMPLATE_CAPACITY <= 254 #define _ZP_HASHMAP_TEMPLATE_INDEX_TYPE uint8_t #define _ZP_HASHMAP_TEMPLATE_INDEX_NONE ((uint8_t)255) #elif _ZP_HASHMAP_TEMPLATE_CAPACITY <= 65534 #define _ZP_HASHMAP_TEMPLATE_INDEX_TYPE uint16_t #define _ZP_HASHMAP_TEMPLATE_INDEX_NONE ((uint16_t)65535) #else #define _ZP_HASHMAP_TEMPLATE_INDEX_TYPE uint32_t #define _ZP_HASHMAP_TEMPLATE_INDEX_NONE ((uint32_t)0xFFFFFFFFu) #endif #ifndef _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME #define _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(x) (void)(x) #endif #ifndef _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME #define _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(x) (void)(x) #endif #ifndef _ZP_HASHMAP_TEMPLATE_KEY_MOVE_FN_NAME #define _ZP_HASHMAP_TEMPLATE_KEY_MOVE_FN_NAME(dst, src) \ *(dst) = *(src); \ _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(src); #endif #ifndef _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME #define _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(dst, src) \ *(dst) = *(src); \ _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(src); #endif // ── Internal name helpers ───────────────────────────────────────────────────── #define _ZP_HASHMAP_TEMPLATE_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, t) #define _ZP_HASHMAP_TEMPLATE_NODE_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, node_t) #define _ZP_HASHMAP_TEMPLATE_INDEX_TYPEDEF _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, index_t) // ── Node ────────────────────────────────────────────────────────────────────── // // Nodes live in a flat pool. The singly-linked-list "next" pointers are stored // in a separate parallel array (_next) rather than inside the node struct. // This avoids the tail-padding that the compiler would otherwise insert after // a small index field to satisfy the alignment requirement of the key/value // types. typedef struct _ZP_HASHMAP_TEMPLATE_NODE_TYPE { _ZP_HASHMAP_TEMPLATE_KEY_TYPE key; _ZP_HASHMAP_TEMPLATE_VAL_TYPE val; } _ZP_HASHMAP_TEMPLATE_NODE_TYPE; // Public typedef for the index type so callers can store/declare indices without // spelling out the internal macro. typedef _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _ZP_HASHMAP_TEMPLATE_INDEX_TYPEDEF; // ── Map type ────────────────────────────────────────────────────────────────── // // _next[i] : index of the next node in the chain for pool slot i, // INDEX_NONE = end-of-chain or free-list end. // _buckets[b] : index of the first node in bucket b, INDEX_NONE = empty. // _free_head : index of the first free pool slot (free list via _next). typedef struct _ZP_HASHMAP_TEMPLATE_TYPE { _ZP_HASHMAP_TEMPLATE_NODE_TYPE _pool[_ZP_HASHMAP_TEMPLATE_CAPACITY]; _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _next[_ZP_HASHMAP_TEMPLATE_CAPACITY]; _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _buckets[_ZP_HASHMAP_TEMPLATE_BUCKET_COUNT]; _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _free_head; size_t _size; // number of live entries } _ZP_HASHMAP_TEMPLATE_TYPE; // ── new ─────────────────────────────────────────────────────────────────────── static inline _ZP_HASHMAP_TEMPLATE_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, new)(void) { _ZP_HASHMAP_TEMPLATE_TYPE map; for (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = 0; b < _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT; b++) { map._buckets[b] = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; } for (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE i = 0; i + 1 < _ZP_HASHMAP_TEMPLATE_CAPACITY; i++) { map._next[i] = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(i + 1); } map._next[_ZP_HASHMAP_TEMPLATE_CAPACITY - 1] = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; // end of free list map._free_head = 0; map._size = 0; return map; } // ── Internal: allocate / free pool node ────────────────────────────────────── static inline _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, pool_alloc)(_ZP_HASHMAP_TEMPLATE_TYPE *map) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = map->_free_head; if (idx == _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { return _ZP_HASHMAP_TEMPLATE_INDEX_NONE; // pool full } map->_free_head = map->_next[idx]; map->_next[idx] = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; return idx; } static inline void _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, pool_free)(_ZP_HASHMAP_TEMPLATE_TYPE *map, _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx) { map->_next[idx] = map->_free_head; map->_free_head = idx; } // ── get_idx ────────────────────────────────────────────────────────────────── // Returns an index of the node for key, or INDEX_NONE if not found. static inline _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, get_idx)(_ZP_HASHMAP_TEMPLATE_TYPE *map, const _ZP_HASHMAP_TEMPLATE_KEY_TYPE *key) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(_ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME(key) % _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT); _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = map->_buckets[b]; while (idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[idx]; if (_ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME(&n->key, key)) { return idx; } idx = map->_next[idx]; } return _ZP_HASHMAP_TEMPLATE_INDEX_NONE; } // ── get ─────────────────────────────────────────────────────────────────────── // Returns a pointer to the value for key, or NULL if not found. static inline _ZP_HASHMAP_TEMPLATE_VAL_TYPE *_ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, get)(_ZP_HASHMAP_TEMPLATE_TYPE *map, const _ZP_HASHMAP_TEMPLATE_KEY_TYPE *key) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, get_idx)(map, key); if (idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { return &map->_pool[idx].val; } return NULL; } // ── contains ───────────────────────────────────────────────────────────────── static inline bool _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, contains)(const _ZP_HASHMAP_TEMPLATE_TYPE *map, const _ZP_HASHMAP_TEMPLATE_KEY_TYPE *key) { return _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, get)((_ZP_HASHMAP_TEMPLATE_TYPE *)(uintptr_t)map, key) != NULL; } // ── size / is_empty ─────────────────────────────────────────────────────────── static inline size_t _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, size)(const _ZP_HASHMAP_TEMPLATE_TYPE *map) { return map->_size; } static inline bool _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, is_empty)(const _ZP_HASHMAP_TEMPLATE_TYPE *map) { return map->_size == 0; } // ── index_valid ────────────────────────────────────────────────────────────── // index_valid: returns true when idx is a live node index (not the sentinel). static inline bool _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, index_valid)(_ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx) { return idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE; } // ── node_at ────────────────────────────────────────────────────────────────── // Converts a valid index to a pointer to its node. // Behaviour is undefined if idx is INDEX_NONE. static inline _ZP_HASHMAP_TEMPLATE_NODE_TYPE *_ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, node_at)(_ZP_HASHMAP_TEMPLATE_TYPE *map, _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx) { return &map->_pool[idx]; } // ── insert ──────────────────────────────────────────────────────────────────── // Takes ownership of *key and *val via move. // If key already exists: old value is destroyed, new value is moved in. // The new key is destroyed (existing key kept). // Returns the index of the inserted/updated node. // Returns INDEX_NONE only when the pool is exhausted and the key is not already // present. Use index_valid() to check the result; use node_at() to obtain // a pointer to the node. static inline _ZP_HASHMAP_TEMPLATE_INDEX_TYPE _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, insert)(_ZP_HASHMAP_TEMPLATE_TYPE *map, _ZP_HASHMAP_TEMPLATE_KEY_TYPE *key, _ZP_HASHMAP_TEMPLATE_VAL_TYPE *val) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(_ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME(key) % _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT); // Walk the chain looking for an existing entry with the same key _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = map->_buckets[b]; while (idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[idx]; if (_ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME(&n->key, key)) { // Update: destroy incoming key, replace value in-place _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(key); _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(&n->val); _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(&n->val, val); return idx; } idx = map->_next[idx]; } // New entry — allocate a pool node _ZP_HASHMAP_TEMPLATE_INDEX_TYPE new_idx = _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, pool_alloc)(map); if (new_idx == _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { return _ZP_HASHMAP_TEMPLATE_INDEX_NONE; // pool exhausted } _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[new_idx]; _ZP_HASHMAP_TEMPLATE_KEY_MOVE_FN_NAME(&n->key, key); _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(&n->val, val); // Prepend to bucket chain (O(1)) map->_next[new_idx] = map->_buckets[b]; map->_buckets[b] = new_idx; map->_size++; return new_idx; } // ── remove_at ──────────────────────────────────────────────────────────────── // Remove the node at the given pool index (obtained from insert or a prior // lookup). Behaviour is undefined if idx is INDEX_NONE or has already been // freed. If out_val != NULL the value is moved out; otherwise it is destroyed. static inline void _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, remove_at)(_ZP_HASHMAP_TEMPLATE_TYPE *map, _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx, _ZP_HASHMAP_TEMPLATE_VAL_TYPE *out_val) { _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[idx]; // Re-derive the bucket from the node's own key so the caller does not need // to supply it separately. _ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(_ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME(&n->key) % _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT); // Walk the chain to find the predecessor and unlink idx. _ZP_HASHMAP_TEMPLATE_INDEX_TYPE prev = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; _ZP_HASHMAP_TEMPLATE_INDEX_TYPE cur = map->_buckets[b]; while (cur != idx) { prev = cur; cur = map->_next[cur]; } if (prev == _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { map->_buckets[b] = map->_next[idx]; // idx was the bucket head } else { map->_next[prev] = map->_next[idx]; } _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(&n->key); if (out_val != NULL) { _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(out_val, &n->val); } else { _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(&n->val); } _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, pool_free)(map, idx); map->_size--; } // ── remove ──────────────────────────────────────────────────────────────────── // If out_val != NULL the value is moved out; otherwise it is destroyed. // Returns true if the key was found. static inline bool _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, remove)(_ZP_HASHMAP_TEMPLATE_TYPE *map, const _ZP_HASHMAP_TEMPLATE_KEY_TYPE *key, _ZP_HASHMAP_TEMPLATE_VAL_TYPE *out_val) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(_ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME(key) % _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT); _ZP_HASHMAP_TEMPLATE_INDEX_TYPE prev = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = map->_buckets[b]; while (idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[idx]; if (_ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME(&n->key, key)) { // Unlink from chain if (prev == _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { map->_buckets[b] = map->_next[idx]; // was the head } else { map->_next[prev] = map->_next[idx]; } _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(&n->key); if (out_val != NULL) { _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME(out_val, &n->val); } else { _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(&n->val); } _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, pool_free)(map, idx); map->_size--; return true; } prev = idx; idx = map->_next[idx]; } return false; } // ── destroy ───────────────────────────────────────────────────────────────────── // Destroys all entries and resets the map for reuse (does not free the map). static inline void _ZP_CAT(_ZP_HASHMAP_TEMPLATE_NAME, destroy)(_ZP_HASHMAP_TEMPLATE_TYPE *map) { // Walk every bucket chain and destroy live entries for (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE b = 0; b < _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT; b++) { _ZP_HASHMAP_TEMPLATE_INDEX_TYPE idx = map->_buckets[b]; while (idx != _ZP_HASHMAP_TEMPLATE_INDEX_NONE) { _ZP_HASHMAP_TEMPLATE_NODE_TYPE *n = &map->_pool[idx]; _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME(&n->key); _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME(&n->val); idx = map->_next[idx]; } map->_buckets[b] = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; } // Rebuild the free list for (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE i = 0; i + 1 < _ZP_HASHMAP_TEMPLATE_CAPACITY; i++) { map->_next[i] = (_ZP_HASHMAP_TEMPLATE_INDEX_TYPE)(i + 1); } map->_next[_ZP_HASHMAP_TEMPLATE_CAPACITY - 1] = _ZP_HASHMAP_TEMPLATE_INDEX_NONE; // end of free list map->_free_head = 0; map->_size = 0; } // ── Undef all macros ────────────────────────────────────────────────────────── #undef _ZP_HASHMAP_TEMPLATE_KEY_TYPE #undef _ZP_HASHMAP_TEMPLATE_VAL_TYPE #undef _ZP_HASHMAP_TEMPLATE_NAME #undef _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT #undef _ZP_HASHMAP_TEMPLATE_CAPACITY #undef _ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_KEY_DESTROY_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_KEY_MOVE_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME #undef _ZP_HASHMAP_TEMPLATE_TYPE #undef _ZP_HASHMAP_TEMPLATE_NODE_TYPE #undef _ZP_HASHMAP_TEMPLATE_INDEX_TYPE #undef _ZP_HASHMAP_TEMPLATE_INDEX_NONE #undef _ZP_HASHMAP_TEMPLATE_INDEX_TYPEDEF ================================================ FILE: include/zenoh-pico/collections/intmap.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_INTMAP_H #define ZENOH_PICO_COLLECTIONS_INTMAP_H #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/hashmap.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif #define _Z_DEFAULT_INT_MAP_CAPACITY 16 typedef _z_hashmap_t _z_int_void_map_t; typedef _z_hashmap_entry_t _z_int_void_map_entry_t; typedef _z_hashmap_iterator_t _z_int_void_map_iterator_t; static inline size_t _z_int_void_map_hash(const void *key) { return *(const size_t *)key; } static inline bool _z_int_void_map_eq(const void *left, const void *right) { const _z_int_void_map_entry_t *l = (_z_int_void_map_entry_t *)left; const _z_int_void_map_entry_t *r = (_z_int_void_map_entry_t *)right; return *(size_t *)l->_key == *(size_t *)r->_key; } static inline void _z_int_void_map_init(_z_int_void_map_t *map, size_t capacity) { _z_hashmap_init(map, capacity, _z_int_void_map_hash, _z_int_void_map_eq); } static inline _z_int_void_map_t _z_int_void_map_make(size_t capacity) { return _z_hashmap_make(capacity, _z_int_void_map_hash, _z_int_void_map_eq); } static inline void *_z_int_void_map_insert(_z_int_void_map_t *map, size_t k, void *v, z_element_free_f f, bool replace) { size_t *key = (size_t *)z_malloc(sizeof(size_t)); if (key == NULL) { _Z_ERROR("Failed to allocate key for intmap."); return NULL; } *key = k; void *ret = _z_hashmap_insert(map, key, v, f, replace); if (ret == NULL) { z_free(key); } return ret; } static inline void *_z_int_void_map_get(const _z_int_void_map_t *map, size_t k) { return _z_hashmap_get(map, &k); } static inline _z_list_t *_z_int_void_map_get_all(const _z_int_void_map_t *map, size_t k) { return _z_hashmap_get_all(map, &k); } static inline void _z_int_void_map_remove(_z_int_void_map_t *map, size_t k, z_element_free_f f) { _z_hashmap_remove(map, &k, f); } static inline size_t _z_int_void_map_capacity(const _z_int_void_map_t *map) { return _z_hashmap_capacity(map); } static inline size_t _z_int_void_map_len(const _z_int_void_map_t *map) { return _z_hashmap_len(map); } static inline bool _z_int_void_map_is_empty(const _z_int_void_map_t *map) { return _z_hashmap_is_empty(map); } static inline z_result_t _z_int_void_map_copy(_z_int_void_map_t *dst, const _z_int_void_map_t *src, z_element_clone_f f_c) { return _z_hashmap_copy(dst, src, f_c); } static inline _z_int_void_map_t _z_int_void_map_clone(const _z_int_void_map_t *src, z_element_clone_f f_c, z_element_free_f f_f) { return _z_hashmap_clone(src, f_c, f_f); } static inline void _z_int_void_map_clear(_z_int_void_map_t *map, z_element_free_f f) { _z_hashmap_clear(map, f); } static inline void _z_int_void_map_free(_z_int_void_map_t **map, z_element_free_f f) { _z_hashmap_free(map, f); } static inline void _z_int_void_map_move(_z_int_void_map_t *dst, _z_int_void_map_t *src) { _z_hashmap_move(dst, src); } static inline _z_int_void_map_iterator_t _z_int_void_map_iterator_make(const _z_int_void_map_t *map) { return _z_hashmap_iterator_make(map); } static inline bool _z_int_void_map_iterator_next(_z_int_void_map_iterator_t *iter) { return _z_hashmap_iterator_next(iter); } static inline size_t _z_int_void_map_iterator_key(const _z_int_void_map_iterator_t *iter) { return *((size_t *)_z_hashmap_iterator_key(iter)); } static inline void *_z_int_void_map_iterator_value(const _z_int_void_map_iterator_t *iter) { return _z_hashmap_iterator_value(iter); } #define _Z_INT_MAP_DEFINE(name, type) \ typedef _z_int_void_map_entry_t name##_intmap_entry_t; \ static inline void name##_intmap_entry_elem_free(void **e) { \ name##_intmap_entry_t *ptr = (name##_intmap_entry_t *)*e; \ if (ptr != NULL) { \ z_free(ptr->_key); \ name##_elem_free(&ptr->_val); \ z_free(ptr); \ *e = NULL; \ } \ } \ static inline void *name##_intmap_entry_elem_clone(const void *e) { \ const name##_intmap_entry_t *src = (name##_intmap_entry_t *)e; \ name##_intmap_entry_t *dst = (name##_intmap_entry_t *)z_malloc(sizeof(name##_intmap_entry_t)); \ if (dst == NULL) { \ _Z_ERROR("Failed to allocate intmap entry clone."); \ return NULL; \ } \ dst->_key = z_malloc(sizeof(size_t)); \ if (dst->_key == NULL) { \ _Z_ERROR("Failed to allocate key for intmap entry clone."); \ z_free(dst); \ return NULL; \ } \ *((size_t *)dst->_key) = *((size_t *)src->_key); \ dst->_val = name##_elem_clone(src->_val); \ return dst; \ } \ typedef _z_int_void_map_t name##_intmap_t; \ typedef _z_int_void_map_iterator_t name##_intmap_iterator_t; \ static inline void name##_intmap_init(name##_intmap_t *m) { \ _z_int_void_map_init(m, _Z_DEFAULT_INT_MAP_CAPACITY); \ } \ static inline name##_intmap_t name##_intmap_make(void) { \ return _z_int_void_map_make(_Z_DEFAULT_INT_MAP_CAPACITY); \ } \ static inline type *name##_intmap_insert(name##_intmap_t *m, size_t k, type *v) { \ return (type *)_z_int_void_map_insert(m, k, v, name##_intmap_entry_elem_free, true); \ } \ static inline type *name##_intmap_insert_push(name##_intmap_t *m, size_t k, type *v) { \ return (type *)_z_int_void_map_insert(m, k, v, name##_intmap_entry_elem_free, false); \ } \ static inline type *name##_intmap_get(const name##_intmap_t *m, size_t k) { \ return (type *)_z_int_void_map_get(m, k); \ } \ static inline _z_list_t *name##_intmap_get_all(const name##_intmap_t *m, size_t k) { \ return _z_int_void_map_get_all(m, k); \ } \ static inline name##_intmap_t name##_intmap_clone(const name##_intmap_t *m) { \ return _z_int_void_map_clone(m, name##_intmap_entry_elem_clone, name##_intmap_entry_elem_free); \ } \ static inline void name##_intmap_remove(name##_intmap_t *m, size_t k) { \ _z_int_void_map_remove(m, k, name##_intmap_entry_elem_free); \ } \ static inline size_t name##_intmap_capacity(name##_intmap_t *m) { return _z_int_void_map_capacity(m); } \ static inline size_t name##_intmap_len(name##_intmap_t *m) { return _z_int_void_map_len(m); } \ static inline bool name##_intmap_is_empty(const name##_intmap_t *m) { return _z_int_void_map_is_empty(m); } \ static inline void name##_intmap_clear(name##_intmap_t *m) { \ _z_int_void_map_clear(m, name##_intmap_entry_elem_free); \ } \ static inline void name##_intmap_free(name##_intmap_t **m) { \ _z_int_void_map_free(m, name##_intmap_entry_elem_free); \ } \ static inline z_result_t name##_intmap_move(name##_intmap_t *dst, name##_intmap_t *src) { \ _z_int_void_map_move(dst, src); \ return _Z_RES_OK; \ } \ static inline name##_intmap_iterator_t name##_intmap_iterator_make(const name##_intmap_t *m) { \ return _z_int_void_map_iterator_make(m); \ } \ static inline bool name##_intmap_iterator_next(name##_intmap_iterator_t *iter) { \ return _z_int_void_map_iterator_next(iter); \ } \ static inline size_t name##_intmap_iterator_key(const name##_intmap_iterator_t *iter) { \ return _z_int_void_map_iterator_key(iter); \ } \ static inline type *name##_intmap_extract(name##_intmap_t *m, size_t k) { \ type *out; \ _z_int_void_map_entry_t e = _z_hashmap_extract(m, &k); \ out = (type *)e._val; \ z_free(e._key); \ return out; \ } \ static inline type *name##_intmap_iterator_value(const name##_intmap_iterator_t *iter) { \ return (type *)_z_int_void_map_iterator_value(iter); \ } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_INTMAP_H */ ================================================ FILE: include/zenoh-pico/collections/lifo.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_LIFO_H #define ZENOH_PICO_COLLECTIONS_LIFO_H #include #include #include "zenoh-pico/collections/element.h" #ifdef __cplusplus extern "C" { #endif /*-------- Ring Buffer --------*/ typedef struct { void **_val; size_t _capacity; size_t _len; } _z_lifo_t; z_result_t _z_lifo_init(_z_lifo_t *lifo, size_t capacity); _z_lifo_t _z_lifo_make(size_t capacity); size_t _z_lifo_capacity(const _z_lifo_t *r); size_t _z_lifo_len(const _z_lifo_t *r); bool _z_lifo_is_empty(const _z_lifo_t *r); bool _z_lifo_is_full(const _z_lifo_t *r); void *_z_lifo_push(_z_lifo_t *r, void *e); void _z_lifo_push_drop(_z_lifo_t *r, void *e, z_element_free_f f); void *_z_lifo_pull(_z_lifo_t *r); _z_lifo_t *_z_lifo_clone(const _z_lifo_t *xs, z_element_clone_f d_f); void _z_lifo_clear(_z_lifo_t *v, z_element_free_f f); void _z_lifo_free(_z_lifo_t **xs, z_element_free_f f_f); #define _Z_LIFO_DEFINE(name, type) \ typedef _z_lifo_t name##_lifo_t; \ static inline z_result_t name##_lifo_init(name##_lifo_t *lifo, size_t capacity) { \ return _z_lifo_init(lifo, capacity); \ } \ static inline name##_lifo_t name##_lifo_make(size_t capacity) { return _z_lifo_make(capacity); } \ static inline size_t name##_lifo_capacity(const name##_lifo_t *r) { return _z_lifo_capacity(r); } \ static inline size_t name##_lifo_len(const name##_lifo_t *r) { return _z_lifo_len(r); } \ static inline bool name##_lifo_is_empty(const name##_lifo_t *r) { return _z_lifo_is_empty(r); } \ static inline bool name##_lifo_is_full(const name##_lifo_t *r) { return _z_lifo_is_full(r); } \ static inline type *name##_lifo_push(name##_lifo_t *r, type *e) { return _z_lifo_push(r, (void *)e); } \ static inline void name##_lifo_push_drop(name##_lifo_t *r, type *e) { \ _z_lifo_push_drop(r, (void *)e, name##_elem_free); \ } \ static inline type *name##_lifo_pull(name##_lifo_t *r) { return (type *)_z_lifo_pull(r); } \ static inline void name##_lifo_clear(name##_lifo_t *r) { _z_lifo_clear(r, name##_elem_free); } \ static inline void name##_lifo_free(name##_lifo_t **r) { _z_lifo_free(r, name##_elem_free); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_LIFO_H */ ================================================ FILE: include/zenoh-pico/collections/list.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_LIST_H #define ZENOH_PICO_COLLECTIONS_LIST_H #include #include #include "zenoh-pico/collections/element.h" #ifdef __cplusplus extern "C" { #endif /*-------- Single-linked List --------*/ /** * A single-linked list. Elements are stored as pointers. * * Members: * void *lal: The pointer to the inner value. * struct z_list *tail: A pointer to the next element in the list. */ typedef struct _z_l_t { void *_val; struct _z_l_t *_next; } _z_list_t; size_t _z_list_len(const _z_list_t *xs); static inline bool _z_list_is_empty(const _z_list_t *xs) { return xs == NULL; } static inline void *_z_list_value(const _z_list_t *xs) { return xs->_val; } static inline _z_list_t *_z_list_next(const _z_list_t *xs) { return xs->_next; } _z_list_t *_z_list_push(_z_list_t *xs, void *x); _z_list_t *_z_list_push_after(_z_list_t *xs, void *x); _z_list_t *_z_list_push_back(_z_list_t *xs, void *x); _z_list_t *_z_list_push_sorted(_z_list_t *xs, z_element_cmp_f c_f, void *x); _z_list_t *_z_list_pop(_z_list_t *xs, z_element_free_f f_f, void **x); _z_list_t *_z_list_find(const _z_list_t *xs, z_element_eq_f f_f, const void *e); _z_list_t *_z_list_drop_element(_z_list_t *list, _z_list_t *prev, z_element_free_f f_f); _z_list_t *_z_list_drop_filter(_z_list_t *xs, z_element_free_f f_f, z_element_eq_f c_f, const void *left, bool only_first); _z_list_t *_z_list_extract_filter(_z_list_t *xs, z_element_eq_f c_f, const void *left, _z_list_t **extracted, bool only_first); _z_list_t *_z_list_clone(const _z_list_t *xs, z_element_clone_f d_f); void _z_list_free(_z_list_t **xs, z_element_free_f f_f); #define _Z_LIST_DEFINE(name, type) \ typedef _z_list_t name##_list_t; \ static inline name##_list_t *name##_list_new(void) { return NULL; } \ static inline size_t name##_list_len(const name##_list_t *l) { return _z_list_len(l); } \ static inline bool name##_list_is_empty(const name##_list_t *l) { return _z_list_is_empty(l); } \ static inline type *name##_list_value(const name##_list_t *l) { return (type *)_z_list_value(l); } \ static inline name##_list_t *name##_list_next(const name##_list_t *l) { return _z_list_next(l); } \ static inline name##_list_t *name##_list_push(name##_list_t *l, type *e) { return _z_list_push(l, e); } \ static inline name##_list_t *name##_list_push_after(name##_list_t *l, type *e) { \ return _z_list_push_after(l, e); \ } \ static inline name##_list_t *name##_list_push_back(name##_list_t *l, type *e) { return _z_list_push_back(l, e); } \ static inline name##_list_t *name##_list_push_sorted(name##_list_t *l, type *e) { \ return _z_list_push_sorted(l, name##_elem_cmp, e); \ } \ static inline name##_list_t *name##_list_pop(name##_list_t *l, type **x) { \ return _z_list_pop(l, name##_elem_free, (void **)x); \ } \ static inline name##_list_t *name##_list_find(const name##_list_t *l, name##_eq_f c_f, const type *e) { \ return _z_list_find(l, (z_element_eq_f)c_f, e); \ } \ static inline name##_list_t *name##_list_drop_element(name##_list_t *l, name##_list_t *p) { \ return _z_list_drop_element(l, p, name##_elem_free); \ } \ static inline name##_list_t *name##_list_drop_first_filter(name##_list_t *l, name##_eq_f c_f, const type *e) { \ return _z_list_drop_filter(l, name##_elem_free, (z_element_eq_f)c_f, e, true); \ } \ static inline name##_list_t *name##_list_drop_all_filter(name##_list_t *l, name##_eq_f c_f, const type *e) { \ return _z_list_drop_filter(l, name##_elem_free, (z_element_eq_f)c_f, e, false); \ } \ static inline name##_list_t *name##_list_extract_all_filter(name##_list_t *l, name##_list_t **extracted, \ name##_eq_f c_f, const type *e) { \ return _z_list_extract_filter(l, (z_element_eq_f)c_f, e, extracted, false); \ } \ static inline name##_list_t *name##_list_extract_first_filter(name##_list_t *l, name##_list_t **extracted, \ name##_eq_f c_f, const type *e) { \ return _z_list_extract_filter(l, (z_element_eq_f)c_f, e, extracted, true); \ } \ static inline name##_list_t *name##_list_clone(name##_list_t *l) { return _z_list_clone(l, name##_elem_clone); } \ static inline void name##_list_free(name##_list_t **l) { _z_list_free(l, name##_elem_free); } /*-------- Sized Single-linked List --------*/ /** * A single-linked list. Elements are stored as value. * * Members: * _z_slist_node_t *data: Pointer to internal data */ // Node struct: {next node_address; generic type} typedef void _z_slist_t; static inline bool _z_slist_is_empty(const _z_slist_t *node) { return node == NULL; } _z_slist_t *_z_slist_push_empty(_z_slist_t *node, size_t value_size); _z_slist_t *_z_slist_push(_z_slist_t *node, const void *value, size_t value_size, z_element_copy_f d_f, bool use_elem_f); _z_slist_t *_z_slist_push_back(_z_slist_t *node, const void *value, size_t value_size, z_element_copy_f d_f, bool use_elem_f); void *_z_slist_value(const _z_slist_t *node); _z_slist_t *_z_slist_next(const _z_slist_t *node); size_t _z_slist_len(const _z_slist_t *node); _z_slist_t *_z_slist_pop(_z_slist_t *node, z_element_clear_f f_f); _z_slist_t *_z_slist_find(const _z_slist_t *node, z_element_eq_f c_f, const void *target_val); _z_slist_t *_z_slist_drop_element(_z_slist_t *list, _z_slist_t *prev, z_element_clear_f f_f); _z_slist_t *_z_slist_drop_filter(_z_slist_t *head, z_element_clear_f f_f, z_element_eq_f c_f, const void *target_val, bool only_first); _z_slist_t *_z_slist_extract_filter(_z_slist_t *head, z_element_eq_f c_f, const void *target_val, _z_slist_t **extracted, bool only_first); _z_slist_t *_z_slist_clone(const _z_slist_t *node, size_t value_size, z_element_copy_f d_f, bool use_elem_f); void _z_slist_free(_z_slist_t **node, z_element_clear_f f); #define _Z_SLIST_DEFINE(name, type, use_elem_f) \ typedef _z_slist_t name##_slist_t; \ static inline name##_slist_t *name##_slist_new(void) { return NULL; } \ static inline size_t name##_slist_len(const name##_slist_t *l) { return _z_slist_len(l); } \ static inline bool name##_slist_is_empty(const name##_slist_t *l) { return _z_slist_is_empty(l); } \ static inline name##_slist_t *name##_slist_next(const name##_slist_t *l) { return _z_slist_next(l); } \ static inline type *name##_slist_value(const name##_slist_t *l) { return (type *)_z_slist_value(l); } \ static inline name##_slist_t *name##_slist_push_empty(name##_slist_t *l) { \ return _z_slist_push_empty(l, sizeof(type)); \ } \ static inline name##_slist_t *name##_slist_push(name##_slist_t *l, const type *e) { \ return _z_slist_push(l, e, sizeof(type), name##_elem_copy, use_elem_f); \ } \ static inline name##_slist_t *name##_slist_push_back(name##_slist_t *l, const type *e) { \ return _z_slist_push_back(l, e, sizeof(type), name##_elem_copy, use_elem_f); \ } \ static inline name##_slist_t *name##_slist_pop(name##_slist_t *l) { return _z_slist_pop(l, name##_elem_clear); } \ static inline name##_slist_t *name##_slist_find(const name##_slist_t *l, name##_eq_f c_f, const type *e) { \ return _z_slist_find(l, (z_element_eq_f)c_f, e); \ } \ static inline name##_slist_t *name##_slist_drop_element(name##_slist_t *l, name##_slist_t *p) { \ return _z_slist_drop_element(l, p, name##_elem_clear); \ } \ static inline name##_slist_t *name##_slist_drop_all_filter(name##_slist_t *l, name##_eq_f c_f, const type *e) { \ return _z_slist_drop_filter(l, name##_elem_clear, (z_element_eq_f)c_f, e, false); \ } \ static inline name##_slist_t *name##_slist_drop_first_filter(name##_slist_t *l, name##_eq_f c_f, const type *e) { \ return _z_slist_drop_filter(l, name##_elem_clear, (z_element_eq_f)c_f, e, true); \ } \ static inline name##_slist_t *name##_slist_extract_all_filter(name##_slist_t *l, name##_slist_t **extracted, \ name##_eq_f c_f, const type *e) { \ return _z_slist_extract_filter(l, (z_element_eq_f)c_f, e, extracted, false); \ } \ static inline name##_slist_t *name##_slist_extract_first_filter(name##_slist_t *l, name##_slist_t **extracted, \ name##_eq_f c_f, const type *e) { \ return _z_slist_extract_filter(l, (z_element_eq_f)c_f, e, extracted, true); \ } \ static inline name##_slist_t *name##_slist_clone(name##_slist_t *l) { \ return _z_slist_clone(l, sizeof(type), name##_elem_copy, use_elem_f); \ } \ static inline void name##_slist_free(name##_slist_t **l) { _z_slist_free(l, name##_elem_clear); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_LIST_H */ ================================================ FILE: include/zenoh-pico/collections/lru_cache.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_LRUCACHE_H #define ZENOH_PICO_COLLECTIONS_LRUCACHE_H #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif // Three way comparison function pointer typedef int (*_z_lru_val_cmp_f)(const void *first, const void *second); // Node struct: {node_data; generic type} typedef void _z_lru_cache_node_t; /*-------- Dynamically allocated vector --------*/ /** * A least recently used cache implementation */ typedef struct _z_lru_cache_t { size_t capacity; // Max number of node size_t len; // Number of node _z_lru_cache_node_t *head; // List head _z_lru_cache_node_t *tail; // List tail _z_lru_cache_node_t **slist; // Sorted node list } _z_lru_cache_t; _z_lru_cache_t _z_lru_cache_init(size_t capacity); void *_z_lru_cache_get(_z_lru_cache_t *cache, void *value, _z_lru_val_cmp_f compare); z_result_t _z_lru_cache_insert(_z_lru_cache_t *cache, void *value, size_t value_size, _z_lru_val_cmp_f compare); void _z_lru_cache_clear(_z_lru_cache_t *cache, z_element_clear_f clear); void _z_lru_cache_delete(_z_lru_cache_t *cache, z_element_clear_f clear); #define _Z_LRU_CACHE_DEFINE(name, type, compare_f) \ typedef _z_lru_cache_t name##_lru_cache_t; \ static inline name##_lru_cache_t name##_lru_cache_init(size_t capacity) { return _z_lru_cache_init(capacity); } \ static inline type *name##_lru_cache_get(name##_lru_cache_t *cache, type *val) { \ return (type *)_z_lru_cache_get(cache, (void *)val, compare_f); \ } \ static inline z_result_t name##_lru_cache_insert(name##_lru_cache_t *cache, type *val) { \ return _z_lru_cache_insert(cache, (void *)val, sizeof(type), compare_f); \ } \ static inline void name##_lru_cache_clear(name##_lru_cache_t *cache) { \ _z_lru_cache_clear(cache, name##_elem_clear); \ } \ static inline void name##_lru_cache_delete(name##_lru_cache_t *cache) { \ _z_lru_cache_delete(cache, name##_elem_clear); \ } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_LRUCACHE_H */ ================================================ FILE: include/zenoh-pico/collections/pqueue_template.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // user needs to define the following macros before including this file: // _ZP_PQUEUE_TEMPLATE_ELEM_TYPE: the type of the elements in the priority queue // _ZP_PQUEUE_TEMPLATE_NAME: the name of the priority queue type to generate (without the _t suffix) // _ZP_PQUEUE_TEMPLATE_SIZE: the maximum size of the priority queue (optional, default is 16) // _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME: the name of the function to destroy an element (optional, default is a // no-op) _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME: the name of the function to move an element (optional, default is // element-wise copy + destroy source) _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME: the name of the comparison function // (elem_a, elem_b) -> int // should return <0 if a has higher priority than b, 0 if equal, >0 if b has higher priority than a // (i.e. min-priority queue by default: smallest element is at the top) // // Optional context support: // _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE: the type of an optional context passed to the compare function. // When defined, the compare macro signature becomes (elem_a, elem_b, ctx_ptr) where ctx_ptr is of type // _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE *. The context is stored inside the queue struct and supplied to every // sift_up / sift_down call automatically. Use new_with_ctx(ctx) to initialise it; new() zero-initialises it. // When not defined (the default), the compare macro keeps its original (elem_a, elem_b) signature and no // context is stored. #include #include #include "zenoh-pico/collections/cat.h" #ifndef _ZP_PQUEUE_TEMPLATE_ELEM_TYPE #error "_ZP_PQUEUE_TEMPLATE_ELEM_TYPE must be defined before including pqueue_template.h" #define _ZP_PQUEUE_TEMPLATE_ELEM_TYPE int #endif #ifndef _ZP_PQUEUE_TEMPLATE_SIZE #define _ZP_PQUEUE_TEMPLATE_SIZE 16 #endif #ifndef _ZP_PQUEUE_TEMPLATE_NAME #define _ZP_PQUEUE_TEMPLATE_NAME _ZP_CAT(_ZP_CAT(_ZP_PQUEUE_TEMPLATE_ELEM_TYPE, pqueue), _ZP_PQUEUE_TEMPLATE_SIZE) #endif #ifndef _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME #error "_ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME must be defined before including pqueue_template.h" #endif #ifndef _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME #define _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME(x) (void)(x) #endif #ifndef _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME #define _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(dst, src) \ *(dst) = *(src); \ _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME(src); #endif // ── Context support ─────────────────────────────────────────────────────────── // Internally the template always calls _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(a, b, ctx_ptr). // When _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE is defined the user-supplied CMP_FN receives the pointer; // otherwise we define the internal macro to call the 2-argument CMP_FN and ignore ctx. #ifdef _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE // Context-aware path: user compare macro is (elem_a, elem_b, ctx_ptr) #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(a, b, pqueue) \ _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME((a), (b), (pqueue->_cmp_ctx)) #else // Context-free path: user compare macro is (elem_a, elem_b); ctx ignored #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(a, b, pqueue) _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME((a), (b)) #endif #define _ZP_PQUEUE_TEMPLATE_TYPE _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, t) typedef struct _ZP_PQUEUE_TEMPLATE_TYPE { _ZP_PQUEUE_TEMPLATE_ELEM_TYPE _buffer[_ZP_PQUEUE_TEMPLATE_SIZE]; size_t _size; #ifdef _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE *_cmp_ctx; #endif } _ZP_PQUEUE_TEMPLATE_TYPE; static inline _ZP_PQUEUE_TEMPLATE_TYPE _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, new)(void) { _ZP_PQUEUE_TEMPLATE_TYPE pqueue = {0}; return pqueue; } #ifdef _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE // new_with_ctx: initialise the queue and store a context pointer for comparisons. static inline _ZP_PQUEUE_TEMPLATE_TYPE _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, new_with_ctx)(_ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE *ctx) { _ZP_PQUEUE_TEMPLATE_TYPE pqueue = {0}; pqueue._cmp_ctx = ctx; return pqueue; } // set_ctx: overwrite the context pointer in an existing queue. // This is useful if the context needs to be updated after the queue is created (for example in case of move of // self-referencing structs), or if new() was used to create a zero-initialised queue and the context pointer needs to // be set later. static inline void _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, set_ctx)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue, _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE *ctx) { pqueue->_cmp_ctx = ctx; } #endif static inline void _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, destroy)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue) { for (size_t i = 0; i < pqueue->_size; i++) { _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME(&pqueue->_buffer[i]); } pqueue->_size = 0; } static inline size_t _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, size)(const _ZP_PQUEUE_TEMPLATE_TYPE *pqueue) { return pqueue->_size; } static inline bool _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, is_empty)(const _ZP_PQUEUE_TEMPLATE_TYPE *pqueue) { return pqueue->_size == 0; } static inline _ZP_PQUEUE_TEMPLATE_ELEM_TYPE *_ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, peek)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue) { if (pqueue->_size == 0) { return NULL; } return &pqueue->_buffer[0]; } static inline void _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, sift_up)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue, size_t i) { while (i > 0) { size_t parent = (i - 1) / 2; if (_ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(&pqueue->_buffer[i], &pqueue->_buffer[parent], pqueue) < 0) { _ZP_PQUEUE_TEMPLATE_ELEM_TYPE tmp; _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&tmp, &pqueue->_buffer[parent]); _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[parent], &pqueue->_buffer[i]); _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[i], &tmp); i = parent; } else { break; } } } static inline void _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, sift_down)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue, size_t i) { while (true) { size_t left = 2 * i + 1; size_t right = 2 * i + 2; size_t best = i; if (left < pqueue->_size && _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(&pqueue->_buffer[left], &pqueue->_buffer[best], pqueue) < 0) { best = left; } if (right < pqueue->_size && _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL(&pqueue->_buffer[right], &pqueue->_buffer[best], pqueue) < 0) { best = right; } if (best == i) { break; } _ZP_PQUEUE_TEMPLATE_ELEM_TYPE tmp; _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&tmp, &pqueue->_buffer[i]); _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[i], &pqueue->_buffer[best]); _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[best], &tmp); i = best; } } static inline bool _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, push)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue, _ZP_PQUEUE_TEMPLATE_ELEM_TYPE *elem) { if (pqueue->_size == _ZP_PQUEUE_TEMPLATE_SIZE) { return false; } _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[pqueue->_size], elem); _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, sift_up)(pqueue, pqueue->_size); pqueue->_size++; return true; } static inline bool _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, pop)(_ZP_PQUEUE_TEMPLATE_TYPE *pqueue, _ZP_PQUEUE_TEMPLATE_ELEM_TYPE *out) { if (pqueue->_size == 0) { return false; } _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(out, &pqueue->_buffer[0]); pqueue->_size--; if (pqueue->_size > 0) { _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME(&pqueue->_buffer[0], &pqueue->_buffer[pqueue->_size]); _ZP_CAT(_ZP_PQUEUE_TEMPLATE_NAME, sift_down)(pqueue, 0); } return true; } #undef _ZP_PQUEUE_TEMPLATE_ELEM_TYPE #undef _ZP_PQUEUE_TEMPLATE_NAME #undef _ZP_PQUEUE_TEMPLATE_SIZE #undef _ZP_PQUEUE_TEMPLATE_ELEM_DESTROY_FN_NAME #undef _ZP_PQUEUE_TEMPLATE_ELEM_MOVE_FN_NAME #undef _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME #undef _ZP_PQUEUE_TEMPLATE_TYPE #ifdef _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE #undef _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE #endif #undef _ZP_PQUEUE_TEMPLATE_ELEM_CMP_INTERNAL ================================================ FILE: include/zenoh-pico/collections/refcount.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_REFCOUNT_H #define ZENOH_PICO_COLLECTIONS_REFCOUNT_H #include #include #include #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_rc_init(void **cnt); z_result_t _z_rc_increase_strong(void *cnt); z_result_t _z_rc_increase_weak(void *cnt); bool _z_rc_decrease_strong(void **cnt); bool _z_rc_decrease_weak(void **cnt); z_result_t _z_rc_weak_upgrade(void *cnt); size_t _z_rc_weak_count(void *cnt); size_t _z_rc_strong_count(void *cnt); void *_z_simple_rc_value(void *rc); z_result_t _z_simple_rc_init(void **rc, const void *val, size_t val_size); z_result_t _z_simple_rc_increase(void *rc); bool _z_simple_rc_decrease(void *rc); size_t _z_simple_rc_strong_count(void *rc); #define _Z_REFCOUNT_DEFINE_STRUCTS(name, type) \ typedef struct name##_rc_t { \ type##_t *_val; \ void *_cnt; \ } name##_rc_t; \ \ typedef struct name##_weak_t { \ type##_t *_val; \ void *_cnt; \ } name##_weak_t; \ static inline void name##_weak_to_rc_inner(const name##_weak_t *weak, name##_rc_t *rc) { \ rc->_val = weak->_val; \ rc->_cnt = weak->_cnt; \ } \ static inline void name##_rc_to_weak_inner(const name##_rc_t *rc, name##_weak_t *weak) { \ weak->_val = rc->_val; \ weak->_cnt = rc->_cnt; \ } \ static inline void name##_rc_clear_inner(name##_rc_t *rc) { type##_clear(rc->_val); } #define _Z_REFCOUNT_DEFINE_NO_FROM_VAL_INNER(name, type) \ \ static inline name##_rc_t name##_rc_null(void) { return (name##_rc_t){0}; } \ static inline name##_weak_t name##_weak_null(void) { return (name##_weak_t){0}; } \ static inline name##_rc_t name##_rc_clone(const name##_rc_t *p) { \ return _z_rc_increase_strong(p->_cnt) == _Z_RES_OK ? *p : name##_rc_null(); \ } \ static inline name##_rc_t *name##_rc_clone_as_ptr(const name##_rc_t *p) { \ name##_rc_t *c = (name##_rc_t *)z_malloc(sizeof(name##_rc_t)); \ if (c != NULL) { \ *c = name##_rc_clone(p); \ if (c->_cnt == NULL) { \ z_free(c); \ c = NULL; \ } \ } \ return c; \ } \ static inline name##_weak_t name##_rc_clone_as_weak(const name##_rc_t *p) { \ if (_z_rc_increase_weak(p->_cnt) == _Z_RES_OK) { \ name##_weak_t ret; \ name##_rc_to_weak_inner(p, &ret); \ return ret; \ } \ return name##_weak_null(); \ } \ static inline name##_weak_t *name##_rc_clone_as_weak_ptr(const name##_rc_t *p) { \ name##_weak_t *c = (name##_weak_t *)z_malloc(sizeof(name##_weak_t)); \ if (c != NULL) { \ *c = name##_rc_clone_as_weak(p); \ if (c->_cnt == NULL) { \ z_free(c); \ c = NULL; \ } \ } \ return c; \ } \ static inline z_result_t name##_rc_copy(name##_rc_t *dst, const name##_rc_t *p) { \ *dst = name##_rc_clone(p); \ if (dst->_cnt == NULL) { \ _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); \ } \ return _Z_RES_OK; \ } \ static inline bool name##_rc_eq(const name##_rc_t *left, const name##_rc_t *right) { \ return (left->_val == right->_val); \ } \ static inline bool name##_rc_decr(name##_rc_t *p) { \ if ((p == NULL) || (p->_cnt == NULL)) { \ return false; \ } \ if (_z_rc_decrease_strong(&p->_cnt)) { \ return true; \ } \ return false; \ } \ static inline bool name##_rc_drop(name##_rc_t *p) { \ if (p == NULL) { \ return false; \ } \ bool res = false; \ if (name##_rc_decr(p) && p->_val != NULL) { \ name##_rc_clear_inner(p); \ z_free(p->_val); \ res = true; \ } \ *p = name##_rc_null(); \ return res; \ } \ static inline name##_weak_t name##_weak_clone(const name##_weak_t *p) { \ return _z_rc_increase_weak(p->_cnt) == _Z_RES_OK ? *p : name##_weak_null(); \ } \ static inline void name##_weak_copy(name##_weak_t *dst, const name##_weak_t *p) { *dst = name##_weak_clone(p); } \ static inline name##_rc_t name##_weak_upgrade(const name##_weak_t *p) { \ if (_z_rc_weak_upgrade(p->_cnt) == _Z_RES_OK) { \ name##_rc_t ret; \ name##_weak_to_rc_inner(p, &ret); \ return ret; \ } \ return name##_rc_null(); \ } \ static inline bool name##_weak_eq(const name##_weak_t *left, const name##_weak_t *right) { \ return (left->_val == right->_val); \ } \ static inline bool name##_weak_drop(name##_weak_t *p) { \ if ((p == NULL) || (p->_cnt == NULL)) { \ return false; \ } \ bool res = false; \ if (_z_rc_decrease_weak(&p->_cnt)) { \ res = true; \ } \ *p = name##_weak_null(); \ return res; \ } \ static inline size_t name##_rc_size(name##_rc_t *p) { \ _ZP_UNUSED(p); \ return sizeof(name##_rc_t); \ } \ static inline size_t name##_rc_strong_count(const name##_rc_t *p) { return _z_rc_strong_count(p->_cnt); } \ static inline size_t name##_rc_weak_count(const name##_rc_t *p) { return _z_rc_weak_count(p->_cnt); } \ static inline size_t name##_weak_strong_count(const name##_weak_t *p) { return _z_rc_strong_count(p->_cnt); } \ static inline size_t name##_weak_weak_count(const name##_weak_t *p) { return _z_rc_weak_count(p->_cnt); } \ static inline name##_t *name##_weak_as_unsafe_ptr(name##_weak_t *p) { return p->_val; } typedef void _z_void_t; typedef void (*_z_void_rc_deleter)(void *); typedef struct _z_void_rc_t { void *_val; void *_cnt; _z_void_rc_deleter _deleter; } _z_void_rc_t; typedef struct _z_void_weak_t { void *_val; void *_cnt; _z_void_rc_deleter _deleter; } _z_void_weak_t; static inline void _z_void_weak_to_rc_inner(const _z_void_weak_t *weak, _z_void_rc_t *rc) { rc->_val = weak->_val; rc->_cnt = weak->_cnt; rc->_deleter = weak->_deleter; } static inline void _z_void_rc_to_weak_inner(const _z_void_rc_t *rc, _z_void_weak_t *weak) { weak->_val = rc->_val; weak->_cnt = rc->_cnt; weak->_deleter = rc->_deleter; } static inline void _z_void_rc_clear_inner(_z_void_rc_t *rc) { if (rc->_deleter != NULL) { rc->_deleter(rc->_val); } } _Z_REFCOUNT_DEFINE_NO_FROM_VAL_INNER(_z_void, _z_void) static inline _z_void_rc_t _z_void_rc_rc_new(void *val, _z_void_rc_deleter deleter) { _z_void_rc_t p = _z_void_rc_null(); if (_z_rc_init(&p._cnt) == _Z_RES_OK) { p._val = val; p._deleter = deleter; } return p; } #define _Z_REFCOUNT_DEFINE_NO_FROM_VAL(name, type) \ _Z_REFCOUNT_DEFINE_STRUCTS(name, type) \ _Z_REFCOUNT_DEFINE_NO_FROM_VAL_INNER(name, type) \ static inline name##_rc_t name##_rc_new(type##_t *val) { \ name##_rc_t p = name##_rc_null(); \ if (_z_rc_init(&p._cnt) == _Z_RES_OK) { \ p._val = val; \ } \ return p; \ } \ static inline void __##name##_z_void_rc_deleter(void *val) { type##_clear((type##_t *)val); } \ static inline _z_void_rc_t name##_rc_to_void(type##_rc_t *rc) { \ _z_void_rc_t p = _z_void_rc_null(); \ p._val = (void *)rc->_val; \ p._cnt = rc->_cnt; \ p._deleter = __##name##_z_void_rc_deleter; \ *rc = name##_rc_null(); \ return p; \ } #define _Z_REFCOUNT_DEFINE(name, type) \ _Z_REFCOUNT_DEFINE_NO_FROM_VAL(name, type) \ static inline name##_rc_t name##_rc_new_from_val(const type##_t *val) { \ type##_t *v = (type##_t *)z_malloc(sizeof(type##_t)); \ if (v == NULL) { \ return name##_rc_null(); \ } \ *v = *val; \ name##_rc_t p = name##_rc_new(v); \ if (p._cnt == NULL) { \ z_free(v); \ return name##_rc_null(); \ } \ return p; \ } \ static inline name##_rc_t name##_rc_new_undefined(void) { \ type##_t *v = (type##_t *)z_malloc(sizeof(type##_t)); \ if (v == NULL) { \ return name##_rc_null(); \ } \ name##_rc_t p = name##_rc_new(v); \ if (p._cnt == NULL) { \ z_free(v); \ return name##_rc_null(); \ } \ return p; \ } #define _Z_SIMPLE_REFCOUNT_DEFINE(name, type) \ typedef struct name##_simple_rc_t { \ void *_val; \ } name##_simple_rc_t; \ \ static inline name##_simple_rc_t name##_simple_rc_null(void) { return (name##_simple_rc_t){0}; } \ \ static inline name##_simple_rc_t name##_simple_rc_new_from_val(const type##_t *val) { \ name##_simple_rc_t p = name##_simple_rc_null(); \ _z_simple_rc_init(&p._val, val, sizeof(type##_t)); \ return p; \ } \ static inline name##_simple_rc_t name##_simple_rc_clone(const name##_simple_rc_t *p) { \ return _z_simple_rc_increase(p->_val) == _Z_RES_OK ? *p : name##_simple_rc_null(); \ } \ static inline name##_simple_rc_t *name##_simple_rc_clone_as_ptr(const name##_simple_rc_t *p) { \ name##_simple_rc_t *c = (name##_simple_rc_t *)z_malloc(sizeof(name##_simple_rc_t)); \ if (c != NULL) { \ *c = name##_simple_rc_clone(p); \ if (c->_val == NULL) { \ z_free(c); \ c = NULL; \ } \ } \ return c; \ } \ static inline void name##_simple_rc_copy(name##_simple_rc_t *dst, const name##_simple_rc_t *p) { \ *dst = name##_simple_rc_clone(p); \ } \ static inline bool name##_simple_rc_eq(const name##_simple_rc_t *left, const name##_simple_rc_t *right) { \ return (left->_val == right->_val); \ } \ static inline bool name##_simple_rc_decr(name##_simple_rc_t *p) { \ if (_z_simple_rc_decrease(p->_val)) { \ return true; \ } \ return false; \ } \ static inline bool name##_simple_rc_drop(name##_simple_rc_t *p) { \ bool res; \ if ((p->_val != NULL) && name##_simple_rc_decr(p)) { \ type##_clear((type##_t *)_z_simple_rc_value(p->_val)); \ z_free(p->_val); \ res = true; \ } else { \ res = false; \ } \ p->_val = NULL; \ return res; \ } \ static inline size_t name##_simple_rc_count(const name##_simple_rc_t *p) { \ return _z_simple_rc_strong_count(p->_val); \ } \ static inline size_t name##_simple_rc_size(name##_simple_rc_t *p) { \ _ZP_UNUSED(p); \ return sizeof(name##_simple_rc_t); \ } \ static inline type##_t *name##_simple_rc_value(const name##_simple_rc_t *p) { \ return (type##_t *)_z_simple_rc_value(p->_val); \ } \ static inline bool name##_simple_rc_is_null(const name##_simple_rc_t *p) { return p->_val == NULL; } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_REFCOUNT_H */ ================================================ FILE: include/zenoh-pico/collections/ring.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_RING_H #define ZENOH_PICO_COLLECTIONS_RING_H #include #include #include "zenoh-pico/collections/element.h" #ifdef __cplusplus extern "C" { #endif /*-------- Ring Buffer --------*/ typedef struct { void **_val; size_t _capacity; size_t _len; size_t _r_idx; size_t _w_idx; } _z_ring_t; /** * Forward iterator of a ring buffer. */ typedef struct { void *_val; const _z_ring_t *_ring; size_t _r_idx; size_t _w_idx; } _z_ring_iterator_t; /** * Reverse iterator of a ring buffer. */ typedef struct { void *_val; const _z_ring_t *_ring; size_t _r_idx; size_t _w_idx; } _z_ring_reverse_iterator_t; z_result_t _z_ring_init(_z_ring_t *ring, size_t capacity); _z_ring_t _z_ring_make(size_t capacity); size_t _z_ring_capacity(const _z_ring_t *r); size_t _z_ring_len(const _z_ring_t *r); bool _z_ring_is_empty(const _z_ring_t *r); bool _z_ring_is_full(const _z_ring_t *r); void *_z_ring_push(_z_ring_t *r, void *e); void *_z_ring_push_force(_z_ring_t *r, void *e); void _z_ring_push_force_drop(_z_ring_t *r, void *e, z_element_free_f f); void *_z_ring_pull(_z_ring_t *r); _z_ring_t *_z_ring_clone(const _z_ring_t *xs, z_element_clone_f d_f); void _z_ring_clear(_z_ring_t *v, z_element_free_f f); void _z_ring_free(_z_ring_t **xs, z_element_free_f f_f); _z_ring_iterator_t _z_ring_iterator_make(const _z_ring_t *ring); bool _z_ring_iterator_next(_z_ring_iterator_t *iter); void *_z_ring_iterator_value(const _z_ring_iterator_t *iter); _z_ring_reverse_iterator_t _z_ring_reverse_iterator_make(const _z_ring_t *ring); bool _z_ring_reverse_iterator_next(_z_ring_reverse_iterator_t *iter); void *_z_ring_reverse_iterator_value(const _z_ring_reverse_iterator_t *iter); #define _Z_RING_DEFINE(name, type) \ typedef _z_ring_t name##_ring_t; \ typedef _z_ring_iterator_t name##_ring_iterator_t; \ typedef _z_ring_reverse_iterator_t name##_ring_reverse_iterator_t; \ static inline z_result_t name##_ring_init(name##_ring_t *ring, size_t capacity) { \ return _z_ring_init(ring, capacity); \ } \ static inline name##_ring_t name##_ring_make(size_t capacity) { return _z_ring_make(capacity); } \ static inline size_t name##_ring_capacity(const name##_ring_t *r) { return _z_ring_capacity(r); } \ static inline size_t name##_ring_len(const name##_ring_t *r) { return _z_ring_len(r); } \ static inline bool name##_ring_is_empty(const name##_ring_t *r) { return _z_ring_is_empty(r); } \ static inline bool name##_ring_is_full(const name##_ring_t *r) { return _z_ring_is_full(r); } \ static inline type *name##_ring_push(name##_ring_t *r, type *e) { return (type *)_z_ring_push(r, (void *)e); } \ static inline type *name##_ring_push_force(name##_ring_t *r, type *e) { \ return (type *)_z_ring_push_force(r, (void *)e); \ } \ static inline void name##_ring_push_force_drop(name##_ring_t *r, type *e) { \ _z_ring_push_force_drop(r, (void *)e, name##_elem_free); \ } \ static inline type *name##_ring_pull(name##_ring_t *r) { return (type *)_z_ring_pull(r); } \ static inline void name##_ring_clear(name##_ring_t *r) { _z_ring_clear(r, name##_elem_free); } \ static inline void name##_ring_free(name##_ring_t **r) { _z_ring_free(r, name##_elem_free); } \ static inline name##_ring_iterator_t name##_ring_iterator_make(const name##_ring_t *ring) { \ return _z_ring_iterator_make(ring); \ } \ static inline bool name##_ring_iterator_next(name##_ring_iterator_t *iter) { return _z_ring_iterator_next(iter); } \ static inline type *name##_ring_iterator_value(const name##_ring_iterator_t *iter) { \ return (type *)_z_ring_iterator_value(iter); \ } \ static inline name##_ring_reverse_iterator_t name##_ring_reverse_iterator_make(const name##_ring_t *ring) { \ return _z_ring_reverse_iterator_make(ring); \ } \ static inline bool name##_ring_reverse_iterator_next(name##_ring_reverse_iterator_t *iter) { \ return _z_ring_reverse_iterator_next(iter); \ } \ static inline type *name##_ring_reverse_iterator_value(const name##_ring_reverse_iterator_t *iter) { \ return (type *)_z_ring_reverse_iterator_value(iter); \ } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_RING_H */ ================================================ FILE: include/zenoh-pico/collections/ring_mt.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_RING_MT_H #define ZENOH_PICO_COLLECTIONS_RING_MT_H #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/fifo.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif /*-------- Ring Buffer Multithreaded --------*/ typedef struct { _z_ring_t _ring; bool is_closed; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex; _z_condvar_t _cv_not_empty; #endif } _z_ring_mt_t; z_result_t _z_ring_mt_init(_z_ring_mt_t *ring, size_t capacity); _z_ring_mt_t *_z_ring_mt_new(size_t capacity); z_result_t _z_ring_mt_close(_z_ring_mt_t *ring); void _z_ring_mt_clear(_z_ring_mt_t *ring, z_element_free_f free_f); void _z_ring_mt_free(_z_ring_mt_t *ring, z_element_free_f free_f); z_result_t _z_ring_mt_push(const void *src, void *context, z_element_free_f element_free); z_result_t _z_ring_mt_pull(void *dst, void *context, z_element_move_f element_move); z_result_t _z_ring_mt_try_pull(void *dst, void *context, z_element_move_f element_move); #ifdef __cplusplus } #endif #endif // ZENOH_PICO_COLLECTIONS_RING_MT_H ================================================ FILE: include/zenoh-pico/collections/seqnumber.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_SEQNUMBER_H #define ZENOH_PICO_COLLECTIONS_SEQNUMBER_H #include #include #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif /* * Compute the next sequence number after `last` in 32-bit serial number space. * Wraps from UINT32_MAX back to 0. */ static inline uint32_t _z_seqnumber_next(uint32_t last) { return (last == UINT32_MAX) ? 0u : (last + 1u); } /* * Test whether `current` is the immediate successor of `last` in * 32-bit serial number space (wrap-safe). */ static inline bool _z_seqnumber_is_next(uint32_t last, uint32_t current) { return (current == _z_seqnumber_next(last)); } /* * Compute the previous sequence number before `current` in 32-bit serial number space. * Wraps from 0 back to UINT32_MAX. */ static inline uint32_t _z_seqnumber_prev(uint32_t current) { return (current == 0u) ? UINT32_MAX : (current - 1u); } /* * Test whether `current` is the immediate predecessor of `last` in * 32-bit serial number space (wrap-safe). */ static inline bool _z_seqnumber_is_prev(uint32_t last, uint32_t current) { return (current == _z_seqnumber_prev(last)); } /* * Compute signed wrap-safe difference between two 32-bit sequence numbers. * Positive if 'a' is newer than 'b', negative if older, zero if equal. * Follows RFC 1982 serial number arithmetic. * * Range: [-2^31+1, 2^31-1] */ static inline int64_t _z_seqnumber_diff(uint32_t a, uint32_t b) { return (int64_t)((int32_t)(a - b)); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_SEQNUMBER_H */ ================================================ FILE: include/zenoh-pico/collections/slice.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_SLICE_H #define ZENOH_PICO_COLLECTIONS_SLICE_H #include #include #include #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif typedef struct { void (*deleter)(void *data, void *context); void *context; } _z_delete_context_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_delete_context_t _z_delete_context_null(void) { return (_z_delete_context_t){0}; } static inline _z_delete_context_t _z_delete_context_create(void (*deleter)(void *context, void *data), void *context) { _z_delete_context_t ret; ret.deleter = deleter; ret.context = context; return ret; } static inline bool _z_delete_context_is_null(const _z_delete_context_t *c) { return c->deleter == NULL; } static inline void _z_delete_context_delete(_z_delete_context_t *c, void *data) { if (!_z_delete_context_is_null(c)) { c->deleter(data, c->context); } } _z_delete_context_t _z_delete_context_default(void); _z_delete_context_t _z_delete_context_static(void); /*-------- Slice --------*/ /** * An array of bytes. * * Members: * size_t len: The length of the bytes array. * uint8_t *start: A pointer to the bytes array. * _z_delete_context_t delete_context - context used to delete the data. */ typedef struct { size_t len; const uint8_t *start; _z_delete_context_t _delete_context; } _z_slice_t; static inline _z_slice_t _z_slice_null(void) { return (_z_slice_t){0}; } static inline void _z_slice_reset(_z_slice_t *bs) { *bs = _z_slice_null(); } static inline bool _z_slice_is_empty(const _z_slice_t *bs) { return bs->len == 0; } static inline bool _z_slice_check(const _z_slice_t *slice) { return slice->start != NULL; } static inline _z_slice_t _z_slice_alias(const _z_slice_t bs) { _z_slice_t ret; ret.len = bs.len; ret.start = bs.start; ret._delete_context = _z_delete_context_null(); return ret; } static inline _z_slice_t _z_slice_from_buf_custom_deleter(const uint8_t *p, size_t len, _z_delete_context_t dc) { _z_slice_t bs; bs.start = p; bs.len = len; bs._delete_context = dc; return bs; } static inline _z_slice_t _z_slice_alias_buf(const uint8_t *p, size_t len) { return _z_slice_from_buf_custom_deleter(p, len, _z_delete_context_null()); } static inline void _z_slice_clear(_z_slice_t *bs) { if (bs->start != NULL) { _z_delete_context_delete(&bs->_delete_context, (void *)bs->start); bs->len = 0; bs->start = NULL; } } z_result_t _z_slice_init(_z_slice_t *bs, size_t capacity); _z_slice_t _z_slice_make(size_t capacity); _z_slice_t _z_slice_copy_from_buf(const uint8_t *bs, size_t len); _z_slice_t _z_slice_steal(_z_slice_t *b); z_result_t _z_slice_copy(_z_slice_t *dst, const _z_slice_t *src); z_result_t _z_slice_n_copy(_z_slice_t *dst, const _z_slice_t *src, size_t offset, size_t len); _z_slice_t _z_slice_duplicate(const _z_slice_t *src); z_result_t _z_slice_move(_z_slice_t *dst, _z_slice_t *src); bool _z_slice_eq(const _z_slice_t *left, const _z_slice_t *right); void _z_slice_free(_z_slice_t **bs); bool _z_slice_is_alloced(const _z_slice_t *s); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_SLICE_H */ ================================================ FILE: include/zenoh-pico/collections/sortedmap.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_SORTEDMAP_H #define ZENOH_PICO_COLLECTIONS_SORTEDMAP_H #include #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif /** * A map entry. * * Members: * void *_key: the key of the entry * void *_val: the value of the entry */ typedef struct { void *_key; void *_val; } _z_sortedmap_entry_t; /** * A sorted map. * * Members: * _z_list_t *_vals: a linked list containing the values * z_element_cmp_f _f_cmp: the function used to compare keys */ typedef struct { _z_list_t *_vals; z_element_cmp_f _f_cmp; } _z_sortedmap_t; /** * Iterator for a generic key-value hashmap. */ typedef struct { _z_sortedmap_entry_t *_entry; const _z_sortedmap_t *_map; _z_list_t *_list_ptr; bool _initialized; } _z_sortedmap_iterator_t; void _z_sortedmap_init(_z_sortedmap_t *map, z_element_cmp_f f_cmp); _z_sortedmap_t _z_sortedmap_make(z_element_cmp_f f_cmp); void *_z_sortedmap_insert(_z_sortedmap_t *map, void *key, void *val, z_element_free_f f, bool replace); void *_z_sortedmap_get(const _z_sortedmap_t *map, const void *key); _z_sortedmap_entry_t *_z_sortedmap_pop_first(_z_sortedmap_t *map); void _z_sortedmap_remove(_z_sortedmap_t *map, const void *key, z_element_free_f f); size_t _z_sortedmap_len(const _z_sortedmap_t *map); bool _z_sortedmap_is_empty(const _z_sortedmap_t *map); z_result_t _z_sortedmap_copy(_z_sortedmap_t *dst, const _z_sortedmap_t *src, z_element_clone_f f_c); _z_sortedmap_t _z_sortedmap_clone(const _z_sortedmap_t *src, z_element_clone_f f_c, z_element_free_f f_f); void _z_sortedmap_clear(_z_sortedmap_t *map, z_element_free_f f); void _z_sortedmap_free(_z_sortedmap_t **map, z_element_free_f f); static inline void _z_sortedmap_move(_z_sortedmap_t *dst, _z_sortedmap_t *src) { *dst = *src; *src = _z_sortedmap_make(src->_f_cmp); } _z_sortedmap_iterator_t _z_sortedmap_iterator_make(const _z_sortedmap_t *map); bool _z_sortedmap_iterator_next(_z_sortedmap_iterator_t *iter); void *_z_sortedmap_iterator_key(const _z_sortedmap_iterator_t *iter); void *_z_sortedmap_iterator_value(const _z_sortedmap_iterator_t *iter); #define _Z_SORTEDMAP_DEFINE_INNER(map_name, key_name, val_name, key_type, val_type) \ typedef _z_sortedmap_entry_t map_name##_sortedmap_entry_t; \ static inline key_type *map_name##_sortedmap_entry_key(const map_name##_sortedmap_entry_t *entry) { \ return (key_type *)entry->_key; \ } \ static inline val_type *map_name##_sortedmap_entry_val(const map_name##_sortedmap_entry_t *entry) { \ return (val_type *)entry->_val; \ } \ static inline void map_name##_sortedmap_entry_free(map_name##_sortedmap_entry_t **entry) { \ map_name##_sortedmap_entry_t *ptr = *entry; \ if (ptr != NULL) { \ key_name##_elem_free(&ptr->_key); \ val_name##_elem_free(&ptr->_val); \ z_free(ptr); \ *entry = NULL; \ } \ } \ static inline void map_name##_sortedmap_entry_elem_free(void **e) { \ map_name##_sortedmap_entry_free((map_name##_sortedmap_entry_t **)e); \ } \ static inline void *map_name##_sortedmap_entry_elem_clone(const void *e) { \ const map_name##_sortedmap_entry_t *src = (map_name##_sortedmap_entry_t *)e; \ map_name##_sortedmap_entry_t *dst = \ (map_name##_sortedmap_entry_t *)z_malloc(sizeof(map_name##_sortedmap_entry_t)); \ dst->_key = key_name##_elem_clone(src->_key); \ dst->_val = val_name##_elem_clone(src->_val); \ return dst; \ } \ typedef _z_sortedmap_t map_name##_sortedmap_t; \ typedef _z_sortedmap_iterator_t map_name##_sortedmap_iterator_t; \ static inline void map_name##_sortedmap_init(map_name##_sortedmap_t *m) { \ _z_sortedmap_init(m, key_name##_elem_cmp); \ } \ static inline map_name##_sortedmap_t map_name##_sortedmap_make(void) { \ return _z_sortedmap_make(key_name##_elem_cmp); \ } \ static inline val_type *map_name##_sortedmap_insert(map_name##_sortedmap_t *m, key_type *k, val_type *v) { \ return (val_type *)_z_sortedmap_insert(m, k, v, map_name##_sortedmap_entry_elem_free, true); \ } \ static inline val_type *map_name##_sortedmap_get(const map_name##_sortedmap_t *m, const key_type *k) { \ return (val_type *)_z_sortedmap_get(m, k); \ } \ static inline map_name##_sortedmap_entry_t *map_name##_sortedmap_pop_first(map_name##_sortedmap_t *m) { \ return (map_name##_sortedmap_entry_t *)_z_sortedmap_pop_first(m); \ } \ static inline z_result_t map_name##_sortedmap_copy(map_name##_sortedmap_t *dst, \ const map_name##_sortedmap_t *src) { \ return _z_sortedmap_copy(dst, src, map_name##_sortedmap_entry_elem_clone); \ } \ static inline map_name##_sortedmap_t map_name##_sortedmap_clone(const map_name##_sortedmap_t *m) { \ return _z_sortedmap_clone(m, map_name##_sortedmap_entry_elem_clone, map_name##_sortedmap_entry_elem_free); \ } \ static inline void map_name##_sortedmap_remove(map_name##_sortedmap_t *m, const key_type *k) { \ _z_sortedmap_remove(m, k, map_name##_sortedmap_entry_elem_free); \ } \ static inline size_t map_name##_sortedmap_len(map_name##_sortedmap_t *m) { return _z_sortedmap_len(m); } \ static inline bool map_name##_sortedmap_is_empty(const map_name##_sortedmap_t *m) { \ return _z_sortedmap_is_empty(m); \ } \ static inline void map_name##_sortedmap_clear(map_name##_sortedmap_t *m) { \ _z_sortedmap_clear(m, map_name##_sortedmap_entry_elem_free); \ } \ static inline void map_name##_sortedmap_free(map_name##_sortedmap_t **m) { \ _z_sortedmap_free(m, map_name##_sortedmap_entry_elem_free); \ } \ static inline z_result_t map_name##_sortedmap_move(map_name##_sortedmap_t *dst, map_name##_sortedmap_t *src) { \ _z_sortedmap_move(dst, src); \ return _Z_RES_OK; \ } \ static inline map_name##_sortedmap_iterator_t map_name##_sortedmap_iterator_make( \ const map_name##_sortedmap_t *m) { \ return _z_sortedmap_iterator_make(m); \ } \ static inline bool map_name##_sortedmap_iterator_next(map_name##_sortedmap_iterator_t *iter) { \ return _z_sortedmap_iterator_next(iter); \ } \ static inline key_type *map_name##_sortedmap_iterator_key(const map_name##_sortedmap_iterator_t *iter) { \ return (key_type *)_z_sortedmap_iterator_key(iter); \ } \ static inline val_type *map_name##_sortedmap_iterator_value(const map_name##_sortedmap_iterator_t *iter) { \ return (val_type *)_z_sortedmap_iterator_value(iter); \ } #define _Z_SORTEDMAP_DEFINE(key_name, val_name, key_type, val_type) \ _Z_SORTEDMAP_DEFINE_INNER(key_name##_##val_name, key_name, val_name, key_type, val_type) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_SORTEDMAP_H */ ================================================ FILE: include/zenoh-pico/collections/string.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_COLLECTIONS_STRING_H #define ZENOH_PICO_COLLECTIONS_STRING_H #include "zenoh-pico/collections/array.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/vec.h" #ifdef __cplusplus extern "C" { #endif /*-------- str --------*/ typedef char *_z_str_t; char *_z_str_clone(const char *src); char *_z_str_n_clone(const char *src, size_t len); void _z_str_clear(char *src); void _z_str_free(char **src); bool _z_str_eq(const char *left, const char *right); int _z_str_cmp(const char *left, const char *right); size_t _z_str_size(const char *src); void _z_str_copy(char *dst, const char *src); void _z_str_n_copy(char *dst, const char *src, size_t size); _Z_ELEM_DEFINE(_z_str, char, _z_str_size, _z_noop_clear, _z_str_copy, _z_noop_move, _z_str_eq, _z_str_cmp, _z_noop_hash) _Z_VEC_DEFINE(_z_str, char) _Z_LIST_DEFINE(_z_str, char) _Z_INT_MAP_DEFINE(_z_str, char) #define INT_STR_MAP_KEYVALUE_SEPARATOR '=' #define INT_STR_MAP_LIST_SEPARATOR ';' typedef struct { char *_str; uint8_t _key; } _z_str_intmapping_t; size_t _z_str_intmap_strlen(const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]); void _z_str_intmap_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]); char *_z_str_intmap_to_str(const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]); z_result_t _z_str_intmap_from_str(_z_str_intmap_t *strint, const char *s, uint8_t argc, _z_str_intmapping_t argv[]); z_result_t _z_str_intmap_from_strn(_z_str_intmap_t *strint, const char *s, uint8_t argc, _z_str_intmapping_t argv[], size_t n); /*-------- string --------*/ /** * A string with no terminator. * */ typedef struct { _z_slice_t _slice; } _z_string_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_string_t _z_string_null(void) { return (_z_string_t){0}; } static inline bool _z_string_check(const _z_string_t *value) { return !_z_slice_is_empty(&value->_slice); } static inline _z_string_t _z_string_alias(const _z_string_t str) { _z_string_t ret; ret._slice = _z_slice_alias(str._slice); return ret; } static inline size_t _z_string_len(const _z_string_t *s) { return s->_slice.len; } static inline const char *_z_string_data(const _z_string_t *s) { return (const char *)s->_slice.start; } static inline bool _z_string_is_empty(const _z_string_t *s) { return s->_slice.len == 0; } static inline _z_string_t _z_string_alias_slice(const _z_slice_t *slice) { _z_string_t s; s._slice = _z_slice_alias(*slice); return s; } static inline _z_string_t _z_string_alias_str(const char *value) { _z_string_t s; s._slice = _z_slice_alias_buf((const uint8_t *)(value), strlen(value)); return s; } static inline _z_string_t _z_string_alias_substr(const char *value, size_t len) { _z_string_t s; s._slice = _z_slice_alias_buf((const uint8_t *)(value), len); return s; } static inline _z_string_t _z_string_from_str_custom_deleter(char *value, _z_delete_context_t c) { _z_string_t s; s._slice = _z_slice_from_buf_custom_deleter((const uint8_t *)(value), strlen(value), c); return s; } static inline void _z_string_reset(_z_string_t *str) { _z_slice_reset(&str->_slice); } static inline void _z_string_clear(_z_string_t *str) { _z_slice_clear(&str->_slice); } _z_string_t _z_string_copy_from_str(const char *value); _z_string_t _z_string_copy_from_substr(const char *value, size_t len); _z_string_t *_z_string_copy_from_str_as_ptr(const char *value); const char *_z_string_rchr(_z_string_t *str, char filter); char *_z_string_pbrk(_z_string_t *str, const char *filter); z_result_t _z_string_copy(_z_string_t *dst, const _z_string_t *src); z_result_t _z_string_copy_substring(_z_string_t *dst, const _z_string_t *src, size_t offset, size_t len); z_result_t _z_string_move(_z_string_t *dst, _z_string_t *src); _z_string_t _z_string_steal(_z_string_t *str); void _z_string_move_str(_z_string_t *dst, char *src); void _z_string_free(_z_string_t **s); int _z_string_compare(const _z_string_t *left, const _z_string_t *right); int _z_substring_compare(const _z_string_t *left, size_t left_start, size_t left_len, const _z_string_t *right, size_t right_start, size_t right_len); bool _z_string_equals(const _z_string_t *left, const _z_string_t *right); _z_string_t _z_string_convert_bytes_le(const _z_slice_t *bs); _z_string_t _z_string_preallocate(const size_t len); z_result_t _z_string_concat_substr(_z_string_t *s, const _z_string_t *left, const char *right, size_t len, const char *separator, size_t separator_len); static inline z_result_t _z_string_concat(_z_string_t *s, const _z_string_t *left, const _z_string_t *right, const char *separator, size_t separator_len) { return _z_string_concat_substr(s, left, _z_string_data(right), _z_string_len(right), separator, separator_len); } char *_z_str_from_string_clone(const _z_string_t *str); _Z_ELEM_DEFINE(_z_string, _z_string_t, _z_string_len, _z_string_clear, _z_string_copy, _z_string_move, _z_string_equals, _z_string_compare, _z_noop_hash) _Z_SVEC_DEFINE(_z_string, _z_string_t) _Z_LIST_DEFINE(_z_string, _z_string_t) _Z_INT_MAP_DEFINE(_z_string, _z_string_t) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_STRING_H */ ================================================ FILE: include/zenoh-pico/collections/sync_group.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_COLLECTIONS_SYNC_GROUP_H #define ZENOH_PICO_COLLECTIONS_SYNC_GROUP_H #include "refcount.h" #include "stdint.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" typedef struct { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t counter_mutex; _z_condvar_t counter_condvar; #endif bool is_closed; } _z_sync_group_state_t; z_result_t _z_sync_group_state_create(_z_sync_group_state_t* sync_group); void _z_sync_group_state_clear(_z_sync_group_state_t* sync_group_state); _Z_REFCOUNT_DEFINE_NO_FROM_VAL(_z_sync_group_state, _z_sync_group_state) typedef struct { _z_sync_group_state_rc_t _state; } _z_sync_group_t; typedef struct { _z_sync_group_state_weak_t _state; } _z_sync_group_notifier_t; static inline _z_sync_group_t _z_sync_group_null(void) { _z_sync_group_t sg = {0}; return sg; } static inline _z_sync_group_notifier_t _z_sync_group_notifier_null(void) { _z_sync_group_notifier_t n = {0}; return n; } static inline _z_sync_group_notifier_t _z_sync_group_notifier_steal(_z_sync_group_notifier_t* notifier) { _z_sync_group_notifier_t n = *notifier; *notifier = _z_sync_group_notifier_null(); return n; } z_result_t _z_sync_group_create(_z_sync_group_t* sync_group); z_result_t _z_sync_group_wait(_z_sync_group_t* sync_group); void _z_sync_group_close(_z_sync_group_t* sync_group); z_result_t _z_sync_group_wait_deadline(_z_sync_group_t* sync_group, const z_clock_t* deadline); static inline bool _z_sync_group_check(const _z_sync_group_t* sync_group) { return !_Z_RC_IS_NULL(&sync_group->_state); } bool _z_sync_group_is_closed(const _z_sync_group_t* sync_group); void _z_sync_group_drop(_z_sync_group_t* sync_group); z_result_t _z_sync_group_create_notifier(const _z_sync_group_t* sync_group, _z_sync_group_notifier_t* notifier); void _z_sync_group_notifier_drop(_z_sync_group_notifier_t* notifier); static inline bool _z_sync_group_notifier_check(const _z_sync_group_notifier_t* notifier) { return !_Z_RC_IS_NULL(¬ifier->_state); } #endif ================================================ FILE: include/zenoh-pico/collections/vec.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_VECTOR_H #define ZENOH_PICO_COLLECTIONS_VECTOR_H #include #include #include "zenoh-pico/collections/element.h" #ifdef __cplusplus extern "C" { #endif /*-------- Dynamically allocated vector --------*/ /** * A dynamically allocated vector. Elements are stored as pointers. */ typedef struct { size_t _capacity; size_t _len; void **_val; } _z_vec_t; static inline _z_vec_t _z_vec_null(void) { return (_z_vec_t){0}; } static inline _z_vec_t _z_vec_alias(const _z_vec_t *src) { return *src; } static inline void *_z_vec_get(const _z_vec_t *v, size_t pos) { assert(pos < v->_len); return v->_val[pos]; } static inline size_t _z_vec_len(const _z_vec_t *v) { return v->_len; } static inline bool _z_vec_is_empty(const _z_vec_t *v) { return v->_len == 0; } _z_vec_t _z_vec_make(size_t capacity); void _z_vec_copy(_z_vec_t *dst, const _z_vec_t *src, z_element_clone_f f); void _z_vec_move(_z_vec_t *dst, _z_vec_t *src); void _z_vec_append(_z_vec_t *v, void *e); void _z_vec_set(_z_vec_t *sv, size_t pos, void *e, z_element_free_f f); void _z_vec_remove(_z_vec_t *sv, size_t pos, z_element_free_f f); void _z_vec_reset(_z_vec_t *v, z_element_free_f f); void _z_vec_clear(_z_vec_t *v, z_element_free_f f); void _z_vec_free(_z_vec_t **v, z_element_free_f f); void _z_vec_release(_z_vec_t *v); #define _Z_VEC_DEFINE(name, type) \ typedef _z_vec_t name##_vec_t; \ static inline name##_vec_t name##_vec_make(size_t capacity) { return _z_vec_make(capacity); } \ static inline size_t name##_vec_len(const name##_vec_t *v) { return _z_vec_len(v); } \ static inline bool name##_vec_is_empty(const name##_vec_t *v) { return _z_vec_is_empty(v); } \ static inline void name##_vec_append(name##_vec_t *v, type *e) { _z_vec_append(v, e); } \ static inline type *name##_vec_get(const name##_vec_t *v, size_t pos) { return (type *)_z_vec_get(v, pos); } \ static inline void name##_vec_set(name##_vec_t *v, size_t pos, type *e) { \ _z_vec_set(v, pos, e, name##_elem_free); \ } \ static inline void name##_vec_remove(name##_vec_t *v, size_t pos) { _z_vec_remove(v, pos, name##_elem_free); } \ static inline void name##_vec_copy(name##_vec_t *dst, const name##_vec_t *src) { \ _z_vec_copy(dst, src, name##_elem_clone); \ } \ static inline name##_vec_t name##_vec_alias(const name##_vec_t *v) { return _z_vec_alias(v); } \ static inline void name##_vec_move(name##_vec_t *dst, name##_vec_t *src) { _z_vec_move(dst, src); } \ static inline void name##_vec_reset(name##_vec_t *v) { _z_vec_reset(v, name##_elem_free); } \ static inline void name##_vec_clear(name##_vec_t *v) { _z_vec_clear(v, name##_elem_free); } \ static inline void name##_vec_free(name##_vec_t **v) { _z_vec_free(v, name##_elem_free); } \ static inline void name##_vec_release(name##_vec_t *v) { _z_vec_release(v); } /*-------- Dynamically allocated sized vector --------*/ /** * A dynamically allocated vector. Elements are stored by value. */ typedef struct { size_t _capacity; size_t _len; void *_val; bool _aliased; } _z_svec_t; static inline _z_svec_t _z_svec_null(void) { return (_z_svec_t){0}; } static inline _z_svec_t _z_svec_alias(const _z_svec_t *src, bool ownership) { _z_svec_t ret; ret._capacity = src->_capacity; ret._len = src->_len; ret._val = src->_val; ret._aliased = !ownership; return ret; } static inline _z_svec_t _z_svec_alias_element(void *element) { _z_svec_t ret; ret._capacity = 1; ret._len = 1; ret._val = element; ret._aliased = true; return ret; } static inline size_t _z_svec_len(const _z_svec_t *v) { return v->_len; } static inline bool _z_svec_is_empty(const _z_svec_t *v) { return v->_len == 0; } static inline void *_z_svec_get(const _z_svec_t *v, size_t i, size_t element_size) { assert(i < v->_len); return (uint8_t *)v->_val + i * element_size; } static inline void *_z_svec_get_mut(_z_svec_t *v, size_t i, size_t element_size) { return (uint8_t *)v->_val + i * element_size; } void _z_svec_init(_z_svec_t *v, size_t offset, size_t element_size); _z_svec_t _z_svec_make(size_t capacity, size_t element_size); z_result_t _z_svec_copy(_z_svec_t *dst, const _z_svec_t *src, z_element_copy_f copy, size_t element_size, bool use_elem_f); void _z_svec_move(_z_svec_t *dst, _z_svec_t *src); z_result_t _z_svec_expand(_z_svec_t *v, z_element_move_f move, size_t element_size, bool use_elem_f); z_result_t _z_svec_append(_z_svec_t *v, const void *e, z_element_move_f m, size_t element_size, bool use_elem_f); void _z_svec_set(_z_svec_t *sv, size_t pos, void *e, z_element_clear_f f, size_t element_size); void _z_svec_remove(_z_svec_t *sv, size_t pos, z_element_clear_f f, z_element_move_f m, size_t element_size, bool use_elem_f); void _z_svec_reset(_z_svec_t *v, z_element_clear_f f, size_t element_size); void _z_svec_clear(_z_svec_t *v, z_element_clear_f f, size_t element_size); void _z_svec_free(_z_svec_t **v, z_element_clear_f f, size_t element_size); void _z_svec_release(_z_svec_t *v); #define _Z_SVEC_DEFINE_NO_COPY(name, type) \ typedef _z_svec_t name##_svec_t; \ static inline name##_svec_t name##_svec_null(void) { return _z_svec_null(); } \ static inline name##_svec_t name##_svec_make(size_t capacity) { return _z_svec_make(capacity, sizeof(type)); } \ static inline void name##_svec_init(name##_svec_t *v, size_t offset) { _z_svec_init(v, offset, sizeof(type)); } \ static inline size_t name##_svec_len(const name##_svec_t *v) { return _z_svec_len(v); } \ static inline bool name##_svec_is_empty(const name##_svec_t *v) { return _z_svec_is_empty(v); } \ static inline z_result_t name##_svec_expand(name##_svec_t *v, bool use_elem_f) { \ return _z_svec_expand(v, name##_elem_move, sizeof(type), use_elem_f); \ } \ static inline z_result_t name##_svec_append(name##_svec_t *v, const type *e, bool use_elem_f) { \ return _z_svec_append(v, e, name##_elem_move, sizeof(type), use_elem_f); \ } \ static inline type *name##_svec_get(const name##_svec_t *v, size_t pos) { \ return (type *)_z_svec_get(v, pos, sizeof(type)); \ } \ static inline type *name##_svec_get_mut(name##_svec_t *v, size_t pos) { \ return (type *)_z_svec_get_mut(v, pos, sizeof(type)); \ } \ static inline void name##_svec_set(name##_svec_t *v, size_t pos, type *e) { \ _z_svec_set(v, pos, e, name##_elem_clear, sizeof(type)); \ } \ static inline void name##_svec_remove(name##_svec_t *v, size_t pos, bool use_elem_f) { \ _z_svec_remove(v, pos, name##_elem_clear, name##_elem_move, sizeof(type), use_elem_f); \ } \ static inline name##_svec_t name##_svec_alias(const name##_svec_t *v) { return _z_svec_alias(v, false); } \ static inline name##_svec_t name##_svec_transfer(name##_svec_t *v) { \ name##_svec_t ret = _z_svec_alias(v, true); \ v->_aliased = true; \ return ret; \ } \ static inline name##_svec_t name##_svec_alias_element(type *e) { return _z_svec_alias_element((void *)e); } \ static inline z_result_t name##_svec_move(name##_svec_t *dst, name##_svec_t *src) { \ _z_svec_move(dst, src); \ return _Z_RES_OK; \ } \ static inline void name##_svec_reset(name##_svec_t *v) { _z_svec_reset(v, name##_elem_clear, sizeof(type)); } \ static inline void name##_svec_clear(name##_svec_t *v) { _z_svec_clear(v, name##_elem_clear, sizeof(type)); } \ static inline void name##_svec_release(name##_svec_t *v) { _z_svec_release(v); } \ static inline void name##_svec_free(name##_svec_t **v) { _z_svec_free(v, name##_elem_clear, sizeof(type)); } #define _Z_SVEC_DEFINE(name, type) \ _Z_SVEC_DEFINE_NO_COPY(name, type) \ static inline z_result_t name##_svec_copy(name##_svec_t *dst, const name##_svec_t *src, bool use_elem_f) { \ return _z_svec_copy(dst, src, name##_elem_copy, sizeof(type), use_elem_f); \ } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COLLECTIONS_VECTOR_H */ ================================================ FILE: include/zenoh-pico/config.h.in ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_CONFIG_H #define INCLUDE_ZENOH_PICO_CONFIG_H #cmakedefine ZP_SYSTEM_PLATFORM_HEADER "@ZP_SYSTEM_PLATFORM_HEADER@" #ifdef ZENOH_GENERIC #include #else /*--- CMake generated config; pass values to CMake to change the following tokens ---*/ #define Z_FRAG_MAX_SIZE @FRAG_MAX_SIZE@ #define Z_BATCH_UNICAST_SIZE @BATCH_UNICAST_SIZE@ #define Z_BATCH_MULTICAST_SIZE @BATCH_MULTICAST_SIZE@ #define Z_CONFIG_SOCKET_TIMEOUT @Z_CONFIG_SOCKET_TIMEOUT@ #define Z_TRANSPORT_LEASE @Z_TRANSPORT_LEASE@ #define Z_TRANSPORT_LEASE_EXPIRE_FACTOR @Z_TRANSPORT_LEASE_EXPIRE_FACTOR@ #define Z_RUNTIME_MAX_TASKS @Z_RUNTIME_MAX_TASKS@ #define Z_TRANSPORT_ACCEPT_TIMEOUT @Z_TRANSPORT_ACCEPT_TIMEOUT@ #define Z_TRANSPORT_CONNECT_TIMEOUT @Z_TRANSPORT_CONNECT_TIMEOUT@ #cmakedefine Z_FEATURE_UNSTABLE_API #define Z_FEATURE_CONNECTIVITY @Z_FEATURE_CONNECTIVITY@ #define Z_FEATURE_MULTI_THREAD @Z_FEATURE_MULTI_THREAD@ #define Z_FEATURE_PUBLICATION @Z_FEATURE_PUBLICATION@ #define Z_FEATURE_ADVANCED_PUBLICATION @Z_FEATURE_ADVANCED_PUBLICATION@ #define Z_FEATURE_SUBSCRIPTION @Z_FEATURE_SUBSCRIPTION@ #define Z_FEATURE_ADVANCED_SUBSCRIPTION @Z_FEATURE_ADVANCED_SUBSCRIPTION@ #define Z_FEATURE_QUERY @Z_FEATURE_QUERY@ #define Z_FEATURE_QUERYABLE @Z_FEATURE_QUERYABLE@ #define Z_FEATURE_LIVELINESS @Z_FEATURE_LIVELINESS@ #define Z_FEATURE_RAWETH_TRANSPORT @Z_FEATURE_RAWETH_TRANSPORT@ #define Z_FEATURE_INTEREST @Z_FEATURE_INTEREST@ #define Z_FEATURE_LINK_TCP @Z_FEATURE_LINK_TCP@ #define Z_FEATURE_LINK_BLUETOOTH @Z_FEATURE_LINK_BLUETOOTH@ #define Z_FEATURE_LINK_WS @Z_FEATURE_LINK_WS@ #define Z_FEATURE_LINK_SERIAL @Z_FEATURE_LINK_SERIAL@ #define Z_FEATURE_LINK_SERIAL_USB @Z_FEATURE_LINK_SERIAL_USB@ #define Z_FEATURE_LINK_TLS @Z_FEATURE_LINK_TLS@ #define Z_FEATURE_SCOUTING @Z_FEATURE_SCOUTING@ #define Z_FEATURE_LINK_UDP_MULTICAST @Z_FEATURE_LINK_UDP_MULTICAST@ #define Z_FEATURE_LINK_UDP_UNICAST @Z_FEATURE_LINK_UDP_UNICAST@ #define Z_FEATURE_MULTICAST_TRANSPORT @Z_FEATURE_MULTICAST_TRANSPORT@ #define Z_FEATURE_UNICAST_TRANSPORT @Z_FEATURE_UNICAST_TRANSPORT@ #define Z_FEATURE_FRAGMENTATION @Z_FEATURE_FRAGMENTATION@ #define Z_FEATURE_ENCODING_VALUES @Z_FEATURE_ENCODING_VALUES@ #define Z_FEATURE_TCP_NODELAY @Z_FEATURE_TCP_NODELAY@ #define Z_FEATURE_LOCAL_SUBSCRIBER @Z_FEATURE_LOCAL_SUBSCRIBER@ #define Z_FEATURE_LOCAL_QUERYABLE @Z_FEATURE_LOCAL_QUERYABLE@ #define Z_FEATURE_SESSION_CHECK @Z_FEATURE_SESSION_CHECK@ #define Z_FEATURE_BATCHING @Z_FEATURE_BATCHING@ #define Z_FEATURE_BATCH_TX_MUTEX @Z_FEATURE_BATCH_TX_MUTEX@ #define Z_FEATURE_BATCH_PEER_MUTEX @Z_FEATURE_BATCH_PEER_MUTEX@ #define Z_FEATURE_MATCHING @Z_FEATURE_MATCHING@ #define Z_FEATURE_RX_CACHE @Z_FEATURE_RX_CACHE@ #define Z_FEATURE_UNICAST_PEER @Z_FEATURE_UNICAST_PEER@ #define Z_FEATURE_AUTO_RECONNECT @Z_FEATURE_AUTO_RECONNECT@ #define Z_FEATURE_MULTICAST_DECLARATIONS @Z_FEATURE_MULTICAST_DECLARATIONS@ #define Z_FEATURE_ADMIN_SPACE @Z_FEATURE_ADMIN_SPACE@ // End of CMake generation #endif /* ZENOH_GENERIC */ /*------------------ Runtime configuration properties ------------------*/ /** * The library mode. * Accepted values : `"client"`, `"peer"`. * Default value : `"client"`. */ #define Z_CONFIG_MODE_KEY 0x40 #define Z_CONFIG_MODE_CLIENT "client" #define Z_CONFIG_MODE_PEER "peer" #define Z_CONFIG_MODE_DEFAULT Z_CONFIG_MODE_CLIENT /** * The locator of a peer to connect to. * Accepted values : `` (ex: `"tcp/10.10.10.10:7447"`). * Default value : None. * Multiple values are accepted in peer to peer unicast mode. */ #define Z_CONFIG_CONNECT_KEY 0x41 /** * A locator to listen on. * Accepted values : `` (ex: `"tcp/10.10.10.10:7447"`). * Default value : None. * Multiple values are not accepted in zenoh-pico. */ #define Z_CONFIG_LISTEN_KEY 0x42 /** * The user name to use for authentication. * Accepted values : ``. * Default value : None. */ #define Z_CONFIG_USER_KEY 0x43 /** * The password to use for authentication. * Accepted values : ``. * Default value : None. */ #define Z_CONFIG_PASSWORD_KEY 0x44 /** * Activates/Deactivates multicast scouting. * Accepted values : `false`, `true`. * Default value : `true`. */ #define Z_CONFIG_MULTICAST_SCOUTING_KEY 0x45 #define Z_CONFIG_MULTICAST_SCOUTING_DEFAULT "true" /** * The multicast address and ports to use for multicast scouting. * Accepted values : `:`. * Default value : `"224.0.0.224:7446"`. */ #define Z_CONFIG_MULTICAST_LOCATOR_KEY 0x46 #define Z_CONFIG_MULTICAST_LOCATOR_DEFAULT "udp/224.0.0.224:7446" /** * In client mode, the period dedicated to scouting a router before failing. * Accepted values : ``. * Default value : `"1000"`. */ #define Z_CONFIG_SCOUTING_TIMEOUT_KEY 0x47 #define Z_CONFIG_SCOUTING_TIMEOUT_DEFAULT "1000" /** * The entities to find in the multicast scouting, defined as a bitwise value. * Accepted values : [0-7]. Bitwise value are defined in :c:enum:`z_whatami_t`. * Default value : `3`. */ #define Z_CONFIG_SCOUTING_WHAT_KEY 0x48 #define Z_CONFIG_SCOUTING_WHAT_DEFAULT "3" /** * A configurable and static Zenoh ID to be used on Zenoh Sessions. * Accepted values : ``. */ #define Z_CONFIG_SESSION_ZID_KEY 0x49 /** * Indicates if data messages should be timestamped. * Accepted values : `false`, `true`. * Default value : `false`. */ #define Z_CONFIG_ADD_TIMESTAMP_KEY 0x4A #define Z_CONFIG_ADD_TIMESTAMP_DEFAULT "false" /*------------------ TLS configuration properties ------------------*/ #define Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY 0x4B #define Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY 0x4C #define Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_KEY 0x4D #define Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY 0x4E #define Z_CONFIG_TLS_LISTEN_CERTIFICATE_KEY 0x4F #define Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY 0x50 #define Z_CONFIG_TLS_ENABLE_MTLS_KEY 0x51 #define Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_KEY 0x52 #define Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_BASE64_KEY 0x53 #define Z_CONFIG_TLS_CONNECT_CERTIFICATE_KEY 0x54 #define Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY 0x55 #define Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY 0x56 /*------------------ Connect behaviour properties ------------------*/ #ifdef Z_FEATURE_UNSTABLE_API /** * The timeout dedicated to establishing connections to configured * connect locators. * * Accepted values : ``. * - `0` : no retry, try each locator once * - `>0` : retry retryable locators until timeout expires * - `-1` : retry indefinitely until a connection is established * * In client mode, this applies to the initial connection attempt and * requires at least one locator to succeed. * * In peer mode, this applies to the initial connection phase for * configured locators. * * Default value : `"0"`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ #define Z_CONFIG_CONNECT_TIMEOUT_KEY 0x57 #endif #define Z_CONFIG_CONNECT_TIMEOUT_DEFAULT "0" #ifdef Z_FEATURE_UNSTABLE_API /** * Indicates whether the application should fail if connection attempts * to configured connect locators do not succeed. * * Accepted values : `false`, `true`. * - `true` : fail if the connection phase does not establish the required connectivity * - `false` : allow the session to continue even if some connections fail * * In client mode, this requires at least one locator to be successfully connected. * * In peer mode, this controls whether failures while connecting configured * peer locators are tolerated. If set to `true`, non-retryable connect errors * fail immediately. Retryable connect errors are retried according to * `Z_CONFIG_CONNECT_TIMEOUT_KEY`; if the timeout expires before the required * connectivity is established, `z_open` fails. * * Default value : `"true"` in client mode, `"false"` in peer mode. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ #define Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY 0x58 #endif #define Z_CONFIG_CONNECT_EXIT_ON_FAILURE_CLIENT_DEFAULT "true" #define Z_CONFIG_CONNECT_EXIT_ON_FAILURE_PEER_DEFAULT "false" /*------------------ Listen behaviour properties ------------------*/ #ifdef Z_FEATURE_UNSTABLE_API /** * The timeout dedicated to opening configured listen locators. * * Accepted values : ``. * - `0` : no retry, try each locator once * - `>0` : retry retryable locators until timeout expires * - `-1` : retry indefinitely until a listener is established * * In peer mode, this applies to the initial listen phase for * configured locators. * * Default value : `"0"`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ #define Z_CONFIG_LISTEN_TIMEOUT_KEY 0x59 #endif #define Z_CONFIG_LISTEN_TIMEOUT_DEFAULT "0" #ifdef Z_FEATURE_UNSTABLE_API /** * Indicates whether the application should fail if configured listen * locators cannot be opened. * * Accepted values : `false`, `true`. * - `true` : fail if the listen phase does not establish the required listeners * - `false` : allow the session to continue even if listen attempts fail * * In peer mode, this applies to the initial listen phase for * configured locators. * If set to `true`, non-retryable listen errors fail immediately. * Retryable listen errors are retried according to * `Z_CONFIG_LISTEN_TIMEOUT_KEY`; if the timeout expires before the * listen locator is opened, `z_open` fails. * * Default value : `"true"`. * * .. warning:: This API has been marked as unstable: it works as advertised, but it may be changed in a future release. */ #define Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY 0x5A #endif #define Z_CONFIG_LISTEN_EXIT_ON_FAILURE_DEFAULT "true" /*------------------ Compile-time configuration properties ------------------*/ /** * Default length for Zenoh ID. Maximum size is 16 bytes. * This configuration will only be applied to Zenoh IDs generated by Zenoh-Pico. */ #define Z_ZID_LENGTH 16 /** * Protocol version identifier. * Do not change this value. */ #define Z_PROTO_VERSION 0x09 /** * Default multicast session join interval in milliseconds. */ #define Z_JOIN_INTERVAL 2500 #define Z_SN_RESOLUTION 0x02 #define Z_REQ_RESOLUTION 0x02 /** * Default size for the rx cache size (if activated). */ #define Z_RX_CACHE_SIZE 10 /** * Default get timeout in milliseconds. */ #define Z_GET_TIMEOUT_DEFAULT 10000 /** * Maximum number of connections for unicast listen sockets. */ #define Z_LISTEN_MAX_CONNECTION_NB 10 /** * Default "nop" instruction */ #define ZP_ASM_NOP __asm__("nop") #endif /* INCLUDE_ZENOH_PICO_CONFIG_H */ ================================================ FILE: include/zenoh-pico/link/config/bt.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_LINK_CONFIG_BT_H #define ZENOH_PICO_LINK_CONFIG_BT_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #define BT_CONFIG_ARGC 3 #define BT_CONFIG_MODE_KEY 0x01 #define BT_CONFIG_MODE_STR "mode" #define BT_CONFIG_PROFILE_KEY 0x02 #define BT_CONFIG_PROFILE_STR "profile" #define BT_CONFIG_TOUT_KEY 0x03 #define BT_CONFIG_TOUT_STR "tout" #define BT_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[BT_CONFIG_ARGC]; \ args[0]._key = BT_CONFIG_MODE_KEY; \ args[0]._str = (char *)BT_CONFIG_MODE_STR; \ args[1]._key = BT_CONFIG_PROFILE_KEY; \ args[1]._str = (char *)BT_CONFIG_PROFILE_STR; \ args[2]._key = BT_CONFIG_TOUT_KEY; \ args[2]._str = (char *)BT_CONFIG_TOUT_STR; size_t _z_bt_config_strlen(const _z_str_intmap_t *s); void _z_bt_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_bt_config_to_str(const _z_str_intmap_t *s); z_result_t _z_bt_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_bt_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_BT_H */ ================================================ FILE: include/zenoh-pico/link/config/raweth.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_CONFIG_RAWETH_H #define ZENOH_PICO_LINK_CONFIG_RAWETH_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/link.h" #ifdef __cplusplus extern "C" { #endif #define RAWETH_SCHEMA "reth" z_result_t _z_endpoint_raweth_valid(_z_endpoint_t *endpoint); z_result_t _z_new_link_raweth(_z_link_t *zl, _z_endpoint_t endpoint); size_t _z_raweth_config_strlen(const _z_str_intmap_t *s); char *_z_raweth_config_to_str(const _z_str_intmap_t *s); z_result_t _z_raweth_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); z_result_t _z_raweth_config_from_str(_z_str_intmap_t *strint, const char *s); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_RAWETH_H */ ================================================ FILE: include/zenoh-pico/link/config/serial.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_CONFIG_SERIAL_H #define ZENOH_PICO_LINK_CONFIG_SERIAL_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_SERIAL == 1 #define SERIAL_CONFIG_ARGC 1 #define SERIAL_CONFIG_BAUDRATE_KEY 0x01 #define SERIAL_CONFIG_BAUDRATE_STR "baudrate" // #define SERIAL_CONFIG_DATABITS_KEY 0x02 // #define SERIAL_CONFIG_DATABITS_STR "data_bits" // #define SERIAL_CONFIG_FLOWCONTROL_KEY 0x03 // #define SERIAL_CONFIG_FLOWCONTROL_STR "flow_control" // #define SERIAL_CONFIG_PARITY_KEY 0x04 // #define SERIAL_CONFIG_PARITY_STR "parity" // #define SERIAL_CONFIG_STOPBITS_KEY 0x05 // #define SERIAL_CONFIG_STOPBITS_STR "stop_bits" // #define SERIAL_CONFIG_TOUT_KEY 0x06 // #define SERIAL_CONFIG_TOUT_STR "tout" #define SERIAL_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[SERIAL_CONFIG_ARGC]; \ args[0]._key = SERIAL_CONFIG_BAUDRATE_KEY; \ args[0]._str = (char *)SERIAL_CONFIG_BAUDRATE_STR; // args[1]._key = SERIAL_CONFIG_DATABITS_KEY; // args[1]._str = SERIAL_CONFIG_DATABITS_STR; // args[2]._key = SERIAL_CONFIG_FLOWCONTROL_KEY; // args[2]._str = SERIAL_CONFIG_FLOWCONTROL_STR; // args[3]._key = SERIAL_CONFIG_PARITY_KEY; // args[3]._str = SERIAL_CONFIG_PARITY_STR; // args[4]._key = SERIAL_CONFIG_STOPBITS_KEY; // args[4]._str = SERIAL_CONFIG_STOPBITS_STR; // args[5]._key = SERIAL_CONFIG_TOUT_KEY; // args[5]._str = SERIAL_CONFIG_TOUT_STR; size_t _z_serial_config_strlen(const _z_str_intmap_t *s); void _z_serial_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_serial_config_to_str(const _z_str_intmap_t *s); z_result_t _z_serial_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_serial_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_SERIAL_H */ ================================================ FILE: include/zenoh-pico/link/config/tcp.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_CONFIG_TCP_H #define ZENOH_PICO_LINK_CONFIG_TCP_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_TCP == 1 #define TCP_CONFIG_ARGC 1 #define TCP_CONFIG_TOUT_KEY 0x01 #define TCP_CONFIG_TOUT_STR "tout" #define TCP_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[TCP_CONFIG_ARGC]; \ args[0]._key = TCP_CONFIG_TOUT_KEY; \ args[0]._str = (char *)TCP_CONFIG_TOUT_STR; size_t _z_tcp_config_strlen(const _z_str_intmap_t *s); void _z_tcp_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_tcp_config_to_str(const _z_str_intmap_t *s); z_result_t _z_tcp_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_tcp_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_TCP_H */ ================================================ FILE: include/zenoh-pico/link/config/tls.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_LINK_CONFIG_TLS_H #define ZENOH_PICO_LINK_CONFIG_TLS_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_TLS == 1 #define TLS_CONFIG_ARGC 12 #define TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY 0x01 #define TLS_CONFIG_ROOT_CA_CERTIFICATE_STR "root_ca_certificate" #define TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_KEY 0x02 #define TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_STR "root_ca_certificate_base64" #define TLS_CONFIG_LISTEN_PRIVATE_KEY_KEY 0x03 #define TLS_CONFIG_LISTEN_PRIVATE_KEY_STR "listen_private_key" #define TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_KEY 0x04 #define TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_STR "listen_private_key_base64" #define TLS_CONFIG_LISTEN_CERTIFICATE_KEY 0x05 #define TLS_CONFIG_LISTEN_CERTIFICATE_STR "listen_certificate" #define TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_KEY 0x06 #define TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_STR "listen_certificate_base64" #define TLS_CONFIG_ENABLE_MTLS_KEY 0x07 #define TLS_CONFIG_ENABLE_MTLS_STR "enable_mtls" #define TLS_CONFIG_CONNECT_PRIVATE_KEY_KEY 0x08 #define TLS_CONFIG_CONNECT_PRIVATE_KEY_STR "connect_private_key" #define TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_KEY 0x09 #define TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_STR "connect_private_key_base64" #define TLS_CONFIG_CONNECT_CERTIFICATE_KEY 0x0A #define TLS_CONFIG_CONNECT_CERTIFICATE_STR "connect_certificate" #define TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_KEY 0x0B #define TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_STR "connect_certificate_base64" #define TLS_CONFIG_VERIFY_NAME_ON_CONNECT_KEY 0x0C #define TLS_CONFIG_VERIFY_NAME_ON_CONNECT_STR "verify_name_on_connect" #define TLS_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[TLS_CONFIG_ARGC]; \ args[0]._key = TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY; \ args[0]._str = (char *)TLS_CONFIG_ROOT_CA_CERTIFICATE_STR; \ args[1]._key = TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_KEY; \ args[1]._str = (char *)TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_STR; \ args[2]._key = TLS_CONFIG_LISTEN_PRIVATE_KEY_KEY; \ args[2]._str = (char *)TLS_CONFIG_LISTEN_PRIVATE_KEY_STR; \ args[3]._key = TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_KEY; \ args[3]._str = (char *)TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_STR; \ args[4]._key = TLS_CONFIG_LISTEN_CERTIFICATE_KEY; \ args[4]._str = (char *)TLS_CONFIG_LISTEN_CERTIFICATE_STR; \ args[5]._key = TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_KEY; \ args[5]._str = (char *)TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_STR; \ args[6]._key = TLS_CONFIG_ENABLE_MTLS_KEY; \ args[6]._str = (char *)TLS_CONFIG_ENABLE_MTLS_STR; \ args[7]._key = TLS_CONFIG_CONNECT_PRIVATE_KEY_KEY; \ args[7]._str = (char *)TLS_CONFIG_CONNECT_PRIVATE_KEY_STR; \ args[8]._key = TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_KEY; \ args[8]._str = (char *)TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_STR; \ args[9]._key = TLS_CONFIG_CONNECT_CERTIFICATE_KEY; \ args[9]._str = (char *)TLS_CONFIG_CONNECT_CERTIFICATE_STR; \ args[10]._key = TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_KEY; \ args[10]._str = (char *)TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_STR; \ args[11]._key = TLS_CONFIG_VERIFY_NAME_ON_CONNECT_KEY; \ args[11]._str = (char *)TLS_CONFIG_VERIFY_NAME_ON_CONNECT_STR; size_t _z_tls_config_strlen(const _z_str_intmap_t *s); void _z_tls_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_tls_config_to_str(const _z_str_intmap_t *s); z_result_t _z_tls_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_tls_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_TLS_H */ ================================================ FILE: include/zenoh-pico/link/config/udp.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_CONFIG_UDP_H #define ZENOH_PICO_LINK_CONFIG_UDP_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #ifdef __cplusplus extern "C" { #endif #define UDP_CONFIG_ARGC 3 #define UDP_CONFIG_IFACE_KEY 0x01 #define UDP_CONFIG_IFACE_STR "iface" #define UDP_CONFIG_TOUT_KEY 0x02 #define UDP_CONFIG_TOUT_STR "tout" #define UDP_CONFIG_JOIN_KEY 0x03 #define UDP_CONFIG_JOIN_STR "join" #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 #define UDP_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[UDP_CONFIG_ARGC]; \ args[0]._key = UDP_CONFIG_IFACE_KEY; \ args[0]._str = (char *)UDP_CONFIG_IFACE_STR; \ args[1]._key = UDP_CONFIG_TOUT_KEY; \ args[1]._str = (char *)UDP_CONFIG_TOUT_STR; \ args[2]._key = UDP_CONFIG_JOIN_KEY; \ args[2]._str = (char *)UDP_CONFIG_JOIN_STR; size_t _z_udp_config_strlen(const _z_str_intmap_t *s); void _z_udp_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_udp_config_to_str(const _z_str_intmap_t *s); z_result_t _z_udp_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_udp_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_UDP_H */ ================================================ FILE: include/zenoh-pico/link/config/ws.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_CONFIG_WS_H #define ZENOH_PICO_LINK_CONFIG_WS_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_WS == 1 #define WS_CONFIG_TOUT_KEY 0x01 #define WS_CONFIG_TOUT_STR "tout" #define WS_CONFIG_MAPPING_BUILD \ uint8_t argc = 1; \ _z_str_intmapping_t args[argc]; \ args[0]._key = WS_CONFIG_TOUT_KEY; \ args[0]._str = WS_CONFIG_TOUT_STR; size_t _z_ws_config_strlen(const _z_str_intmap_t *s); void _z_ws_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s); char *_z_ws_config_to_str(const _z_str_intmap_t *s); z_result_t _z_ws_config_from_str(_z_str_intmap_t *strint, const char *s); z_result_t _z_ws_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_CONFIG_WS_H */ ================================================ FILE: include/zenoh-pico/link/endpoint.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_ENDPOINT_H #define ZENOH_PICO_LINK_ENDPOINT_H #include "zenoh-pico/collections/array.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif /*------------------ Locator ------------------*/ #if Z_FEATURE_LINK_TCP == 1 #define TCP_SCHEMA "tcp" #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 #define UDP_SCHEMA "udp" #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #define BT_SCHEMA "bt" #endif #if Z_FEATURE_LINK_SERIAL == 1 #define SERIAL_SCHEMA "serial" #endif #if Z_FEATURE_LINK_WS == 1 #define WS_SCHEMA "ws" #endif #if Z_FEATURE_LINK_TLS == 1 #define TLS_SCHEMA "tls" #endif #define LOCATOR_PROTOCOL_SEPARATOR '/' #define LOCATOR_METADATA_SEPARATOR '?' typedef struct { _z_str_intmap_t _metadata; _z_string_t _protocol; _z_string_t _address; } _z_locator_t; bool _z_locator_eq(const _z_locator_t *left, const _z_locator_t *right); void _z_locator_init(_z_locator_t *locator); _z_string_t _z_locator_to_string(const _z_locator_t *loc); z_result_t _z_locator_from_string(_z_locator_t *lc, const _z_string_t *s); size_t _z_locator_size(_z_locator_t *lc); void _z_locator_clear(_z_locator_t *lc); _Z_ELEM_DEFINE(_z_locator, _z_locator_t, _z_locator_size, _z_locator_clear, _z_noop_copy, _z_noop_move, _z_locator_eq, _z_noop_cmp, _z_noop_hash) /*------------------ Locator array ------------------*/ _Z_ARRAY_DEFINE(_z_locator, _z_locator_t) /*------------------ Endpoint ------------------*/ #define ENDPOINT_CONFIG_SEPARATOR '#' typedef struct { _z_locator_t _locator; _z_str_intmap_t _config; } _z_endpoint_t; _z_string_t _z_endpoint_to_string(const _z_endpoint_t *e); z_result_t _z_endpoint_from_string(_z_endpoint_t *ep, const _z_string_t *s); void _z_endpoint_clear(_z_endpoint_t *ep); void _z_endpoint_free(_z_endpoint_t **ep); char *_z_endpoint_parse_host(const _z_string_t *addr); char *_z_endpoint_parse_port(const _z_string_t *addr); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_ENDPOINT_H */ ================================================ FILE: include/zenoh-pico/link/link.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_H #define ZENOH_PICO_LINK_H #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/bt.h" #include "zenoh-pico/link/transport/raweth.h" #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/link/transport/udp_unicast.h" #include "zenoh-pico/link/transport/ws.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_LINK_SERIAL == 1 #include "zenoh-pico/link/transport/serial_protocol.h" #endif #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/transport/tls_stream.h" #endif #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif /** * Link transport capability enum. * * Enumerators: * Z_LINK_CAP_TRANSPORT_UNICAST: Link has unicast capabilities. * Z_LINK_CAP_TRANSPORT_MULTICAST: Link has multicast capabilities. */ typedef enum { Z_LINK_CAP_TRANSPORT_UNICAST = 0, Z_LINK_CAP_TRANSPORT_MULTICAST = 1, Z_LINK_CAP_TRANSPORT_RAWETH = 2, } _z_link_cap_transport_t; /** * Link flow capability enum. * * Enumerators: * Z_LINK_CAP_FLOW_DATAGRAM: Link uses datagrams. * Z_LINK_CAP_FLOW_STREAM: Link uses a byte stream. */ typedef enum { Z_LINK_CAP_FLOW_DATAGRAM = 0, Z_LINK_CAP_FLOW_STREAM = 1, } _z_link_cap_flow_t; /** * Link capabilities, stored as a register-like object. * * Fields: * transport: 2 bits, see _z_link_cap_transport_t enum. * flow: 1 bit, see _z_link_cap_flow_t enum. * reliable: 1 bit, 1 if the link is reliable (network definition) * reserved: 4 bits, reserved for future use */ typedef struct _z_link_capabilities_t { uint8_t _transport : 2; uint8_t _flow : 1; uint8_t _is_reliable : 1; uint8_t _reserved : 4; } _z_link_capabilities_t; struct _z_link_t; // Forward declaration to be used in _z_f_link_* typedef z_result_t (*_z_f_link_open)(struct _z_link_t *self); typedef z_result_t (*_z_f_link_listen)(struct _z_link_t *self); typedef void (*_z_f_link_close)(struct _z_link_t *self); typedef size_t (*_z_f_link_write)(const struct _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket); typedef size_t (*_z_f_link_write_all)(const struct _z_link_t *self, const uint8_t *ptr, size_t len); typedef size_t (*_z_f_link_read)(const struct _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr); typedef size_t (*_z_f_link_read_exact)(const struct _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket); typedef size_t (*_z_f_link_read_socket)(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len); typedef void (*_z_f_link_free)(struct _z_link_t *self); static inline size_t _z_noop_link_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { _ZP_UNUSED(socket); _ZP_UNUSED(ptr); _ZP_UNUSED(len); _Z_ERROR("Function not implemented"); return 0; } enum _z_link_type_e { _Z_LINK_TYPE_TCP, _Z_LINK_TYPE_UDP, _Z_LINK_TYPE_BT, _Z_LINK_TYPE_SERIAL, _Z_LINK_TYPE_WS, _Z_LINK_TYPE_TLS, _Z_LINK_TYPE_RAWETH, }; typedef struct _z_link_t { _z_endpoint_t _endpoint; int _type; union { #if Z_FEATURE_LINK_TCP == 1 _z_tcp_socket_t _tcp; #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 _z_udp_socket_t _udp; #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 _z_bt_socket_t _bt; #endif #if Z_FEATURE_LINK_SERIAL == 1 _z_serial_socket_t _serial; #endif #if Z_FEATURE_LINK_WS == 1 _z_ws_socket_t _ws; #endif #if Z_FEATURE_LINK_TLS == 1 _z_tls_socket_t _tls; #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 _z_raweth_socket_t _raweth; #endif } _socket; _z_f_link_open _open_f; _z_f_link_listen _listen_f; _z_f_link_close _close_f; _z_f_link_write _write_f; _z_f_link_write_all _write_all_f; _z_f_link_read _read_f; _z_f_link_read_exact _read_exact_f; _z_f_link_read_socket _read_socket_f; _z_f_link_free _free_f; uint16_t _mtu; _z_link_capabilities_t _cap; } _z_link_t; void _z_link_clear(_z_link_t *zl); void _z_link_free(_z_link_t **zl); z_result_t _z_open_socket(const _z_string_t *locator, const _z_config_t *session_cfg, _z_sys_net_socket_t *socket); z_result_t _z_open_link(_z_link_t *zl, const _z_string_t *locator, const _z_config_t *session_cfg); z_result_t _z_listen_link(_z_link_t *zl, const _z_string_t *locator, const _z_config_t *session_cfg); z_result_t _z_link_send_wbuf(const _z_link_t *zl, const _z_wbuf_t *wbf, _z_sys_net_socket_t *socket); size_t _z_link_recv_zbuf(const _z_link_t *zl, _z_zbuf_t *zbf, _z_slice_t *addr); size_t _z_link_recv_exact_zbuf(const _z_link_t *zl, _z_zbuf_t *zbf, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket); size_t _z_link_socket_recv_zbuf(const _z_link_t *link, _z_zbuf_t *zbf, const _z_sys_net_socket_t socket); const _z_sys_net_socket_t *_z_link_get_socket(const _z_link_t *link); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_H */ ================================================ FILE: include/zenoh-pico/link/manager.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_MANAGER_H #define ZENOH_PICO_LINK_MANAGER_H #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/link.h" #include "zenoh-pico/utils/config.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_endpoint_tcp_valid(_z_endpoint_t *ep); z_result_t _z_new_peer_tcp(_z_endpoint_t *endpoint, _z_sys_net_socket_t *socket); z_result_t _z_new_link_tcp(_z_link_t *zl, _z_endpoint_t *ep); #if Z_FEATURE_LINK_UDP_UNICAST == 1 z_result_t _z_endpoint_udp_unicast_valid(_z_endpoint_t *ep); z_result_t _z_new_link_udp_unicast(_z_link_t *zl, _z_endpoint_t ep); #endif #if Z_FEATURE_LINK_UDP_MULTICAST == 1 z_result_t _z_endpoint_udp_multicast_valid(_z_endpoint_t *ep); z_result_t _z_new_link_udp_multicast(_z_link_t *zl, _z_endpoint_t ep); #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 z_result_t _z_endpoint_bt_valid(_z_endpoint_t *ep); z_result_t _z_new_link_bt(_z_link_t *zl, _z_endpoint_t ep); #endif #if Z_FEATURE_LINK_SERIAL == 1 z_result_t _z_endpoint_serial_valid(_z_endpoint_t *ep); z_result_t _z_new_link_serial(_z_link_t *zl, _z_endpoint_t ep); #endif #if Z_FEATURE_LINK_WS == 1 z_result_t _z_endpoint_ws_valid(_z_endpoint_t *ep); z_result_t _z_new_link_ws(_z_link_t *zl, _z_endpoint_t *ep); #endif #if Z_FEATURE_LINK_TLS == 1 z_result_t _z_endpoint_tls_valid(_z_endpoint_t *ep); z_result_t _z_new_peer_tls(_z_endpoint_t *endpoint, _z_sys_net_socket_t *socket, const _z_config_t *session_cfg); z_result_t _z_new_link_tls(_z_link_t *zl, _z_endpoint_t *ep, const _z_config_t *session_cfg); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_MANAGER_H */ ================================================ FILE: include/zenoh-pico/link/transport/bt.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_BT_H #define ZENOH_PICO_LINK_TRANSPORT_BT_H #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #if Z_FEATURE_LINK_BLUETOOTH == 1 #ifdef __cplusplus extern "C" { #endif #define _Z_BT_MODE_MASTER 0 #define _Z_BT_MODE_SLAVE 1 #define _Z_BT_PROFILE_UNSUPPORTED 255 #define _Z_BT_PROFILE_SPP 0 typedef struct { _z_sys_net_socket_t _sock; char *_gname; } _z_bt_socket_t; z_result_t _z_open_bt(_z_sys_net_socket_t *sock, const char *gname, uint8_t mode, uint8_t profile, uint32_t tout); z_result_t _z_listen_bt(_z_sys_net_socket_t *sock, const char *gname, uint8_t mode, uint8_t profile, uint32_t tout); void _z_close_bt(_z_sys_net_socket_t *sock); size_t _z_read_exact_bt(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_read_bt(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_send_bt(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len); #ifdef __cplusplus } #endif #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_BT_H */ ================================================ FILE: include/zenoh-pico/link/transport/lwip_socket.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_LWIP_SOCKET_H #define ZENOH_PICO_LINK_TRANSPORT_LWIP_SOCKET_H #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if defined(ZP_PLATFORM_SOCKET_LWIP) && defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) && \ !defined(ZP_LWIP_SOCKET_HELPERS_DEFINED) #error "LWIP socket helpers must be provided by the selected platform header" #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_LWIP_SOCKET_H */ ================================================ FILE: include/zenoh-pico/link/transport/raweth.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_RAWETH_H #define ZENOH_PICO_LINK_TRANSPORT_RAWETH_H #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 // Ethernet types (big endian) #define _ZP_ETH_TYPE_VLAN 0x0081 // Address Sizes #define _ZP_MAC_ADDR_LENGTH 6 // Max frame size #define _ZP_MAX_ETH_FRAME_SIZE 1514 // Endpoint config types typedef struct { _z_string_t _keyexpr; uint16_t _vlan; // vlan tag (pcp + dei + id), big endian uint8_t _dmac[_ZP_MAC_ADDR_LENGTH]; bool _has_vlan; } _zp_raweth_mapping_entry_t; void _z_raweth_clear_mapping_entry(_zp_raweth_mapping_entry_t *entry); _Z_ELEM_DEFINE(_zp_raweth_mapping, _zp_raweth_mapping_entry_t, _z_noop_size, _z_raweth_clear_mapping_entry, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_ARRAY_DEFINE(_zp_raweth_mapping, _zp_raweth_mapping_entry_t) typedef struct { uint8_t _mac[_ZP_MAC_ADDR_LENGTH]; } _zp_raweth_whitelist_entry_t; _Z_ELEM_DEFINE(_zp_raweth_whitelist, _zp_raweth_whitelist_entry_t, _z_noop_size, _z_noop_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_ARRAY_DEFINE(_zp_raweth_whitelist, _zp_raweth_whitelist_entry_t) // Ethernet header structure type typedef struct { uint8_t dmac[_ZP_MAC_ADDR_LENGTH]; // Destination mac address uint8_t smac[_ZP_MAC_ADDR_LENGTH]; // Source mac address uint16_t ethtype; // Ethertype of frame uint16_t data_length; // Payload length } _zp_eth_header_t; typedef struct { uint8_t dmac[_ZP_MAC_ADDR_LENGTH]; // Destination mac address uint8_t smac[_ZP_MAC_ADDR_LENGTH]; // Source mac address uint16_t vlan_type; // Vlan ethtype uint16_t tag; // Vlan tag uint16_t ethtype; // Ethertype of frame uint16_t data_length; // Payload length } _zp_eth_vlan_header_t; typedef struct { const char *_interface; _z_sys_net_socket_t _sock; _zp_raweth_mapping_array_t _mapping; _zp_raweth_whitelist_array_t _whitelist; uint16_t _vlan; uint16_t _ethtype; uint8_t _dmac[_ZP_MAC_ADDR_LENGTH]; uint8_t _smac[_ZP_MAC_ADDR_LENGTH]; bool _has_vlan; } _z_raweth_socket_t; z_result_t _z_open_raweth(_z_sys_net_socket_t *sock, const char *interface); size_t _z_send_raweth(const _z_sys_net_socket_t *sock, const void *buff, size_t buff_len); size_t _z_receive_raweth(const _z_sys_net_socket_t *sock, void *buff, size_t buff_len, _z_slice_t *addr, const _zp_raweth_whitelist_array_t *whitelist); z_result_t _z_close_raweth(_z_sys_net_socket_t *sock); uint16_t _z_raweth_ntohs(uint16_t val); uint16_t _z_raweth_htons(uint16_t val); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_RAWETH_H */ ================================================ FILE: include/zenoh-pico/link/transport/serial.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_SERIAL_H #define ZENOH_PICO_LINK_TRANSPORT_SERIAL_H #include #include #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate); z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate); z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate); z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate); void _z_serial_close(_z_sys_net_socket_t *sock); // flawfinder: ignore size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_SERIAL_H */ ================================================ FILE: include/zenoh-pico/link/transport/serial_protocol.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_SERIAL_PROTOCOL_H #define ZENOH_PICO_LINK_TRANSPORT_SERIAL_PROTOCOL_H #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_SERIAL == 1 #define _Z_SERIAL_MTU_SIZE 1500 #define _Z_SERIAL_MFS_SIZE _Z_SERIAL_MTU_SIZE + 1 + 2 + 4 // MTU + Header + Serial Len + Serial CRC32 #define _Z_SERIAL_MAX_COBS_BUF_SIZE \ 1516 // Max On-the-wire length for an MFS/MTU of 1510/1500 (MFS + Overhead Byte (OHB) + End of packet (EOP)) typedef struct { _z_sys_net_socket_t _sock; } _z_serial_socket_t; z_result_t _z_serial_endpoint_valid(const _z_endpoint_t *endpoint); z_result_t _z_serial_protocol_open(_z_serial_socket_t *sock, const _z_endpoint_t *endpoint); z_result_t _z_serial_protocol_listen(_z_serial_socket_t *sock, const _z_endpoint_t *endpoint); void _z_serial_protocol_close(_z_serial_socket_t *sock); z_result_t _z_connect_serial(const _z_sys_net_socket_t sock); size_t _z_read_serial(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_send_serial(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len); size_t _z_read_exact_serial(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_SERIAL_PROTOCOL_H */ ================================================ FILE: include/zenoh-pico/link/transport/socket.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_LINK_TRANSPORT_SOCKET_H #define ZENOH_PICO_LINK_TRANSPORT_SOCKET_H #include #include #include #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif typedef struct _z_socket_wait_iter_t _z_socket_wait_iter_t; typedef void (*_z_socket_wait_iter_reset_f)(_z_socket_wait_iter_t *iter); typedef bool (*_z_socket_wait_iter_next_f)(_z_socket_wait_iter_t *iter); typedef const _z_sys_net_socket_t *(*_z_socket_wait_iter_get_socket_f)(const _z_socket_wait_iter_t *iter); typedef void (*_z_socket_wait_iter_set_ready_f)(_z_socket_wait_iter_t *iter, bool ready); struct _z_socket_wait_iter_t { void *_ctx; void *_current_entry; _z_socket_wait_iter_reset_f _reset; _z_socket_wait_iter_next_f _next; _z_socket_wait_iter_get_socket_f _get_socket; _z_socket_wait_iter_set_ready_f _set_ready; }; static inline void _z_socket_wait_iter_reset(_z_socket_wait_iter_t *iter) { iter->_reset(iter); } static inline bool _z_socket_wait_iter_next(_z_socket_wait_iter_t *iter) { return iter->_next(iter); } static inline const _z_sys_net_socket_t *_z_socket_wait_iter_get_socket(const _z_socket_wait_iter_t *iter) { return iter->_get_socket(iter); } static inline void _z_socket_wait_iter_set_ready(_z_socket_wait_iter_t *iter, bool ready) { iter->_set_ready(iter, ready); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms); z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking); z_result_t _z_ip_port_to_endpoint(const uint8_t *address, size_t address_len, uint16_t port, char *dst, size_t dst_len); z_result_t _z_socket_get_endpoints(const _z_sys_net_socket_t *sock, char *local, size_t local_len, char *remote, size_t remote_len); void _z_socket_close(_z_sys_net_socket_t *sock); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_SOCKET_H */ ================================================ FILE: include/zenoh-pico/link/transport/tcp.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_TCP_H #define ZENOH_PICO_LINK_TRANSPORT_TCP_H #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif typedef struct { _z_sys_net_socket_t _sock; _z_sys_net_endpoint_t _rep; } _z_tcp_socket_t; char *_z_tcp_address_parse_host(const _z_string_t *address); z_result_t _z_tcp_address_valid(const _z_string_t *address); z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port); void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep); z_result_t _z_tcp_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address); // flawfinder: ignore z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout); z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint); z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out); void _z_tcp_close(_z_sys_net_socket_t *sock); // flawfinder: ignore size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_TCP_H */ ================================================ FILE: include/zenoh-pico/link/transport/tls_stream.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_TLS_STREAM_H #define ZENOH_PICO_LINK_TRANSPORT_TLS_STREAM_H #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/link/transport/tcp.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_TLS == 1 #include "mbedtls/entropy.h" #include "mbedtls/error.h" #include "mbedtls/hmac_drbg.h" #include "mbedtls/net_sockets.h" #include "mbedtls/pk.h" #include "mbedtls/ssl.h" #include "mbedtls/x509_crt.h" typedef struct { mbedtls_ssl_context _ssl; mbedtls_ssl_config _ssl_config; mbedtls_entropy_context _entropy; mbedtls_hmac_drbg_context _hmac_drbg; mbedtls_x509_crt _ca_cert; mbedtls_pk_context _listen_key; mbedtls_x509_crt _listen_cert; mbedtls_pk_context _client_key; mbedtls_x509_crt _client_cert; bool _enable_mtls; } _z_tls_context_t; typedef struct { _z_sys_net_socket_t _sock; _z_tls_context_t *_tls_ctx; bool _is_peer_socket; // a peer socket allocated in heap, needs to be freed in _z_close_tls } _z_tls_socket_t; z_result_t _z_open_tls(_z_tls_socket_t *sock, const _z_sys_net_endpoint_t *rep, const char *hostname, const _z_str_intmap_t *config, bool peer_socket); z_result_t _z_listen_tls(_z_tls_socket_t *sock, const _z_sys_net_endpoint_t *rep, const _z_str_intmap_t *config); z_result_t _z_tls_accept(_z_sys_net_socket_t *socket, const _z_sys_net_socket_t *listen_sock); void _z_close_tls_socket(_z_sys_net_socket_t *socket); void _z_close_tls(_z_tls_socket_t *sock); size_t _z_read_tls(const _z_tls_socket_t *sock, uint8_t *ptr, size_t len); size_t _z_write_tls(const _z_tls_socket_t *sock, const uint8_t *ptr, size_t len); size_t _z_write_all_tls(const _z_tls_socket_t *sock, const uint8_t *ptr, size_t len); #endif // Z_FEATURE_LINK_TLS == 1 #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_TLS_STREAM_H */ ================================================ FILE: include/zenoh-pico/link/transport/udp_multicast.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_UDP_MULTICAST_H #define ZENOH_PICO_LINK_TRANSPORT_UDP_MULTICAST_H #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/link/transport/udp_unicast.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_UDP_MULTICAST == 1 static inline z_result_t _z_udp_multicast_default_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_unicast_endpoint_init_from_address(ep, address); } static inline void _z_udp_multicast_default_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_unicast_endpoint_clear(ep); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address); void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep); // flawfinder: ignore z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface); z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join); void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep); size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep); // flawfinder: ignore size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep); size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_UDP_MULTICAST_H */ ================================================ FILE: include/zenoh-pico/link/transport/udp_unicast.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_UDP_UNICAST_H #define ZENOH_PICO_LINK_TRANSPORT_UDP_UNICAST_H #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif typedef struct { _z_sys_net_socket_t _sock; _z_sys_net_socket_t _msock; _z_sys_net_endpoint_t _rep; _z_sys_net_endpoint_t _lep; } _z_udp_socket_t; z_result_t _z_udp_unicast_address_valid(const _z_string_t *address); z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port); void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep); z_result_t _z_udp_unicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address); // flawfinder: ignore z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout); z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout); void _z_udp_unicast_close(_z_sys_net_socket_t *sock); // flawfinder: ignore size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len); size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_UDP_UNICAST_H */ ================================================ FILE: include/zenoh-pico/link/transport/ws.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_WS_H #define ZENOH_PICO_LINK_TRANSPORT_WS_H #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LINK_WS == 1 typedef struct { _z_sys_net_socket_t _sock; _z_sys_net_endpoint_t _rep; } _z_ws_socket_t; z_result_t _z_ws_endpoint_init(_z_sys_net_endpoint_t *ep, const _z_string_t *address); void _z_ws_endpoint_clear(_z_sys_net_endpoint_t *ep); z_result_t _z_ws_transport_open(_z_ws_socket_t *sock, uint32_t tout); z_result_t _z_ws_transport_listen(_z_ws_socket_t *sock); void _z_ws_transport_close(_z_ws_socket_t *sock); size_t _z_ws_transport_read(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len); size_t _z_ws_transport_read_exact(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len); size_t _z_ws_transport_write(const _z_ws_socket_t *sock, const uint8_t *ptr, size_t len); size_t _z_ws_transport_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_WS_H */ ================================================ FILE: include/zenoh-pico/net/config.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_CONFIG_NETAPI_H #define ZENOH_PICO_CONFIG_NETAPI_H #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/config.h" #ifdef __cplusplus extern "C" { #endif /** * Create an empty set of properties for zenoh-net session configuration. * * Returns: * A :c:type:`_z_config_t` containing an empty configuration. */ _z_config_t _z_config_empty(void); /** * Create a default set of properties for zenoh-net session configuration. * * Parameters: * config: A :c:type:`_z_config_t` where a default configuration for client mode will be constructed. * * Returns: * `0`` in case of success, or a ``negative value`` otherwise. */ z_result_t _z_config_default(_z_config_t *config); /** * Create a default set of properties for client mode zenoh-net session configuration. * If peer is not null, it is added to the configuration as remote peer. * * Parameters: * config: A :c:type:`_z_config_t` where a default configuration for client mode will be constructed. * locator: An optional peer locator. The caller keeps its ownership. * * Returns: * `0`` in case of success, or a ``negative value`` otherwise. */ z_result_t _z_config_client(_z_config_t *config, const char *locator); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_CONFIG_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/encoding.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_ENCODING_NETAPI_H #define ZENOH_PICO_ENCODING_NETAPI_H #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/string.h" #ifdef __cplusplus extern "C" { #endif #define _Z_ENCODING_ID_DEFAULT 0 /** * A zenoh encoding. */ typedef struct _z_encoding_t { _z_string_t schema; uint16_t id; } _z_encoding_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_encoding_t _z_encoding_null(void) { return (_z_encoding_t){0}; } static inline bool _z_encoding_check(const _z_encoding_t *encoding) { return ((encoding->id != _Z_ENCODING_ID_DEFAULT) || _z_string_check(&encoding->schema)); } static inline void _z_encoding_clear(_z_encoding_t *encoding) { _z_string_clear(&encoding->schema); } _z_encoding_t _z_encoding_wrap(uint16_t id, const char *schema); z_result_t _z_encoding_make(_z_encoding_t *encoding, uint16_t id, const char *schema, size_t len); z_result_t _z_encoding_copy(_z_encoding_t *dst, const _z_encoding_t *src); z_result_t _z_encoding_move(_z_encoding_t *dst, _z_encoding_t *src); static inline _z_encoding_t _z_encoding_alias(const _z_encoding_t *src) { _z_encoding_t dst; dst.id = src->id; if (_z_string_check(&src->schema)) { dst.schema = _z_string_alias(src->schema); } else { dst.schema = _z_string_null(); } return dst; } static inline _z_encoding_t _z_encoding_steal(_z_encoding_t *val) { _z_encoding_t ret = *val; val->schema._slice.len = 0; val->schema._slice.start = NULL; return ret; } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_ENCODING_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/filtering.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_FILTERING_NETAPI_H #define ZENOH_PICO_FILTERING_NETAPI_H #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/utils/locality.h" #ifdef __cplusplus extern "C" { #endif typedef struct { uintptr_t peer; uint32_t decl_id; } _z_filter_target_t; typedef z_element_eq_f _z_filter_target_eq_f; typedef z_element_eq_f _z_filter_target_predicate_f; #define _z_filter_target_elem_copy _z_noop_copy #define _z_filter_target_elem_clear _z_noop_clear _Z_SLIST_DEFINE(_z_filter_target, _z_filter_target_t, false) typedef enum { WRITE_FILTER_ACTIVE = 0, WRITE_FILTER_OFF = 1, } _z_write_filter_state_t; struct _z_write_filter_registration_t; typedef enum { _Z_WRITE_FILTER_SUBSCRIBER = 0, _Z_WRITE_FILTER_QUERYABLE = 1, } _z_write_filter_target_type_t; typedef struct { _z_session_weak_t zn; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t mutex; #endif _z_filter_target_slist_t *targets; #if Z_FEATURE_MATCHING == 1 _z_closure_matching_status_intmap_t callbacks; #endif _z_keyexpr_t key; uint8_t state; bool is_complete; bool is_aggregate; bool allow_local; bool allow_remote; _z_write_filter_target_type_t target_type; size_t local_targets; struct _z_write_filter_registration_t *registration; } _z_write_filter_ctx_t; z_result_t _z_write_filter_ctx_clear(_z_write_filter_ctx_t *filter); _Z_REFCOUNT_DEFINE_NO_FROM_VAL(_z_write_filter_ctx, _z_write_filter_ctx) /** * Return type when declaring a queryable. */ typedef struct _z_write_filter_t { uint32_t _interest_id; _z_write_filter_ctx_rc_t ctx; } _z_write_filter_t; z_result_t _z_write_filter_create(const _z_session_rc_t *zn, _z_write_filter_t *filter, const _z_declared_keyexpr_t *keyexpr, uint8_t interest_flag, bool complete, z_locality_t locality); z_result_t _z_write_filter_clear(_z_write_filter_t *filter); void _z_write_filter_notify_subscriber(struct _z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool add); void _z_write_filter_notify_queryable(struct _z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool is_complete, bool add); #if Z_FEATURE_MATCHING z_result_t _z_write_filter_ctx_add_callback(_z_write_filter_ctx_t *filter, size_t id, _z_closure_matching_status_t *v); void _z_write_filter_ctx_remove_callback(_z_write_filter_ctx_t *filter, size_t id); void _z_write_filter_ctx_remove_callbacks(_z_write_filter_ctx_t *ctx); #endif #if Z_FEATURE_INTEREST == 1 static inline bool _z_write_filter_ctx_active(const _z_write_filter_ctx_t *ctx) { return ctx->state == WRITE_FILTER_ACTIVE; } static inline bool _z_write_filter_active(const _z_write_filter_t *filter) { return _z_write_filter_ctx_active(_Z_RC_IN_VAL(&filter->ctx)); } #else static inline bool _z_write_filter_active(const _z_write_filter_t *filter) { _ZP_UNUSED(filter); return false; } #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_FILTERING_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/liveliness.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef INCLUDE_ZENOH_PICO_NET_LIVELINESS_H #define INCLUDE_ZENOH_PICO_NET_LIVELINESS_H #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/net/subscribe.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_LIVELINESS == 1 z_result_t _z_declare_liveliness_token(const _z_session_rc_t *zn, _z_liveliness_token_t *ret_token, const _z_declared_keyexpr_t *keyexpr); z_result_t _z_undeclare_liveliness_token(_z_liveliness_token_t *token); #if Z_FEATURE_SUBSCRIPTION == 1 /** * Declare a :c:type:`_z_subscriber_t` for the given liveliness key. * * Parameters: * subscriber: The subscriber to initialize. * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to subscribe. * callback: The callback function that will be called each time a matching liveliness token changed. * history: Enable current interest to return history tokens. * arg: A pointer that will be passed to the **callback** on each call. * * Returns: * 0 in case of success, negative error code otherwise. */ z_result_t _z_declare_liveliness_subscriber(_z_subscriber_t *subscriber, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, bool history, void *arg); z_result_t _z_register_liveliness_subscriber(uint32_t *out_sub_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, bool history, void *arg, const _z_sync_group_t *opt_callback_drop_sync_group); /** * Undeclare a liveliness :c:type:`_z_subscriber_t`. * * Parameters: * sub: The :c:type:`_z_subscriber_t` to undeclare. The callee releases the * subscriber upon successful return. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_liveliness_subscriber(_z_subscriber_t *sub); #endif // Z_FEATURE_SUBSCRIPTION == 1 #if Z_FEATURE_QUERY == 1 /** * Query liveliness token state. * * Parameters: * session: The zenoh-net session. * keyexpr: The resource key to liveliness token. * callback: The callback function that will be called on reception of replies for this query. * dropper: The callback function that will be called on upon completion of the callback. * arg: A pointer that will be passed to the **callback** on each call. * timeout_ms: The timeout value of this query. * opt_cancellation_token: Optional cancellation token, can be NULL. */ z_result_t _z_liveliness_query(const _z_session_rc_t *session, const _z_declared_keyexpr_t *keyexpr, _z_closure_reply_callback_t callback, _z_drop_handler_t dropper, void *arg, uint64_t timeout_ms, _z_cancellation_token_rc_t *opt_cancellation_token); #endif // Z_FEATURE_QUERY == 1 #endif // Z_FEATURE_LIVELINESS == 1 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_NET_LIVELINESS_H */ ================================================ FILE: include/zenoh-pico/net/logger.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LOGGER_NETAPI_H #define ZENOH_PICO_LOGGER_NETAPI_H #ifdef __cplusplus extern "C" { #endif /** * Initialise the zenoh runtime logger */ void _z_init_logger(void); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LOGGER_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/matching.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_NET_MATCHING_H #define INCLUDE_ZENOH_PICO_NET_MATCHING_H #include "zenoh-pico/net/filtering.h" #ifdef __cplusplus extern "C" { #endif typedef struct _z_matching_listener_t { uint32_t _id; _z_write_filter_ctx_weak_t _write_filter_ctx; } _z_matching_listener_t; #if Z_FEATURE_MATCHING == 1 z_result_t _z_matching_listener_declare(_z_matching_listener_t *listener, _z_session_t *s, const _z_write_filter_ctx_rc_t *ctx, _z_closure_matching_status_t *callback); z_result_t _z_matching_listener_register(uint32_t *listener_id, _z_session_t *s, const _z_write_filter_ctx_rc_t *ctx, _z_closure_matching_status_t *callback); z_result_t _z_matching_listener_undeclare(_z_matching_listener_t *listener); // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_matching_listener_t _z_matching_listener_null(void) { return (_z_matching_listener_t){0}; } static inline bool _z_matching_listener_check(const _z_matching_listener_t *matching_listener) { return !_Z_RC_IS_NULL(&matching_listener->_write_filter_ctx); } void _z_matching_listener_clear(_z_matching_listener_t *listener); void _z_matching_listener_free(_z_matching_listener_t **listener); #endif // Z_FEATURE_MATCHING == 1 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_NET_MATCHING_H */ ================================================ FILE: include/zenoh-pico/net/primitives.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef INCLUDE_ZENOH_PICO_NET_PRIMITIVES_H #define INCLUDE_ZENOH_PICO_NET_PRIMITIVES_H #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/net/publish.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/net/subscribe.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif /*------------- Declaration Helpers --------------*/ z_result_t _z_send_declare(_z_session_t *zn, const _z_network_message_t *n_msg); z_result_t _z_send_undeclare(_z_session_t *zn, const _z_network_message_t *n_msg); /*------------------ Discovery ------------------*/ #if Z_FEATURE_SCOUTING == 1 /** * Scout for routers and/or peers. * * Parameters: * what: A what bitmask of zenoh entities kind to scout for. * zid: The ZenohID of the scouting origin. * locator: The locator where to scout. * timeout: The time that should be spent scouting before returning the results. */ void _z_scout(const z_what_t what, const _z_id_t zid, _z_string_t *locator, const uint32_t timeout, _z_closure_hello_callback_t callback, void *arg_call, _z_drop_handler_t dropper, void *arg_drop); #endif /*------------------ Declarations ------------------*/ /** * Associate a numerical id with the given resource key. * * This numerical id will be used on the network to save bandwidth and * ease the retrieval of the concerned resource in the routing tables. * * Parameters: * zn: The zenoh-net session. The caller keeps its ownership. * key: The resource key to map to a numerical id. The callee gets * the ownership of any allocated value. * out_id: The memory location where to store the numerical id of the declared resource. * * Returns: * 0 in case of success, or a negative value identifying the error. */ z_result_t _z_declare_resource(_z_session_t *zn, const _z_string_t *key, uint16_t *out_id); /** * Associate a numerical id with the given resource key. * * This numerical id will be used on the network to save bandwidth and * ease the retrieval of the concerned resource in the routing tables. * * Parameters: * zn: The zenoh-net session. The caller keeps its ownership. * rid: The numerical id of the resource to undeclare. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_resource(_z_session_t *zn, uint16_t rid); /** * Declare keyexpr if it is necessary and allowed. * Returns updated keyexpr. * * Parameters: * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to declare. * Returns: * Updated keyexpr. */ _z_keyexpr_t _z_update_keyexpr_to_declared(_z_session_t *zs, _z_keyexpr_t keyexpr); #if Z_FEATURE_PUBLICATION == 1 /** * Declare a :c:type:`_z_publisher_t` for the given resource key. * * Written resources that match the given key will only be sent on the network * if matching subscribers exist in the system. * * Parameters: * publisher: The publisher to initialize. * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to publish. * encoding: The optional default encoding to use during put. The callee gets the ownership. * reliability: The reliability of the publisher messages * * Returns: * 0 in case of success, negative error code otherwise. */ z_result_t _z_declare_publisher(_z_publisher_t *publisher, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_encoding_t *encoding, z_congestion_control_t congestion_control, z_priority_t priority, bool is_express, z_reliability_t reliability, z_locality_t allowed_destination); /** * Undeclare a :c:type:`_z_publisher_t`. * * Parameters: * pub: The :c:type:`_z_publisher_t` to undeclare. The callee releases the * publisher upon successful return. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_publisher(_z_publisher_t *pub); /** * Write data corresponding to a given resource key, allowing the definition of * additional properties. * * Parameters: * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to write. The caller keeps its ownership. * payload: The data to write. * encoding: The encoding of the payload. The callee gets the ownership of * any allocated value. * kind: The kind of the value. * cong_ctrl: The congestion control of this write. Possible values defined * in :c:type:`_z_congestion_control_t`. * is_express: If true, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * timestamp: The timestamp of this write. The API level timestamp (e.g. of the data when it was created). * attachment: An optional attachment to this write. * reliability: The message reliability. * source_info: The message source info. * allowed_destination: The allowed destination locality. * Returns: * ``0`` in case of success, ``-1`` in case of failure. */ z_result_t _z_write(_z_session_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const z_sample_kind_t kind, const z_congestion_control_t cong_ctrl, z_priority_t priority, bool is_express, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info, z_locality_t allowed_destination); #endif #if Z_FEATURE_SUBSCRIPTION == 1 /** * Declare a :c:type:`_z_subscriber_t` for the given resource key. * * Parameters: * subscriber: The subscriber to initialize. * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to subscribe. * callback: The callback function that will be called each time a data matching the subscribed resource is * received. * dropper: A function that will be called once subscriber is undeclared. * arg: A pointer that will be passed to the **callback** on each call. * * Returns: * 0 in case of success, negative error code otherwise. */ z_result_t _z_declare_subscriber(_z_subscriber_t *subscriber, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin); z_result_t _z_register_subscriber(uint32_t *out_sub_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin, const _z_sync_group_t *opt_callback_drop_sync_group); /** * Undeclare a :c:type:`_z_subscriber_t`. * * Parameters: * sub: The :c:type:`_z_subscriber_t` to undeclare. The callee releases the * subscriber upon successful return. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_subscriber(_z_subscriber_t *sub); #endif #if Z_FEATURE_QUERYABLE == 1 /** * Declare a :c:type:`_z_queryable_t` for the given resource key. * * Parameters: * queryable: The queryable to initialize. * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key the :c:type:`_z_queryable_t` will reply to. * complete: The complete of :c:type:`_z_queryable_t`. * callback: The callback function that will be called each time a matching query is received. * arg: A pointer that will be passed to the **callback** on each call. * * Returns: * 0 in case of success, negative error code otherwise. */ z_result_t _z_declare_queryable(_z_queryable_t *queryable, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, bool complete, _z_closure_query_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin); z_result_t _z_register_queryable(uint32_t *queryable_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, bool complete, _z_closure_query_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin, const _z_sync_group_t *sync_group); /** * Undeclare a :c:type:`_z_queryable_t`. * * Parameters: * qle: The :c:type:`_z_queryable_t` to undeclare. The callee releases the * queryable upon successful return. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_queryable(_z_queryable_t *qle); /** * Send a reply to a query. * * This function must be called inside of a Queryable callback passing the * query received as parameters of the callback function. This function can * be called multiple times to send multiple replies to a query. The reply * will be considered complete when the Queryable callback returns. * * Parameters: * query: The query to reply to. The caller keeps its ownership. * key: The resource key of this reply. The caller keeps the ownership. * payload: The value of this reply, the caller keeps ownership. * kind: The type of operation. * attachment: An optional attachment to the reply. * is_express: If true, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * timestamp: The timestamp of this reply. The API level timestamp (e.g. of the data when it was created). * source_info: The message source info. */ z_result_t _z_send_reply(const _z_query_t *query, const _z_session_rc_t *zsrc, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const z_sample_kind_t kind, bool is_express, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, _z_source_info_t *source_info); /** * Send a reply error to a query. * * This function must be called inside of a Queryable callback passing the * query received as parameters of the callback function. This function can * be called multiple times to send multiple replies to a query. The reply * will be considered complete when the Queryable callback returns. * * Parameters: * query: The query to reply to. The caller keeps its ownership. * key: The resource key of this reply. The caller keeps the ownership. * payload: The value of this reply, the caller keeps ownership. */ z_result_t _z_send_reply_err(const _z_query_t *query, const _z_session_rc_t *zsrc, _z_bytes_t *payload, _z_encoding_t *encoding); #endif #if Z_FEATURE_QUERY == 1 /** * Declare a :c:type:`_z_querier_t` for the given resource key. * * Parameters: * querier: The querie to initialize. * zn: The zenoh-net session. The caller keeps its ownership. * keyexpr: The resource key to query. * consolidation_mode: The kind of consolidation that should be applied on replies. * congestion_control: The congestion control to apply when routing the querier queries. * target: The kind of queryables that should be target of this query. * priority: The priority of the query. * is_express: If true, Zenoh will not wait to batch this operation with others to reduce the bandwidth. * timeout_ms: The timeout value of this query. * encoding: The optional default encoding to use during query. The callee gets the ownership. * reliability: The reliability of the querier messages. * allowed_destination: Locality restrictions for delivery. * accept_replies: The accepted replies for this querier. * Returns: * 0 in case of success, negative error code otherwise. */ z_result_t _z_declare_querier(_z_querier_t *querier, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, z_consolidation_mode_t consolidation_mode, z_congestion_control_t congestion_control, z_query_target_t target, z_priority_t priority, bool is_express, uint64_t timeout_ms, _z_encoding_t *encoding, z_reliability_t reliability, z_locality_t allowed_destination, z_reply_keyexpr_t accept_replies); /** * Undeclare a :c:type:`_z_querier_t`. * * Parameters: * querier: The :c:type:`_z_querier_t` to undeclare. The callee releases the * querier upon successful return. * Returns: * 0 if success, or a negative value identifying the error. */ z_result_t _z_undeclare_querier(_z_querier_t *querier); /** * Query data from the matching queryables in the system. * * Parameters: * session: The zenoh-net session. * querier_id: Optional id of querier. * keyexpr: The resource key to query. * parameters: An indication to matching queryables about the queried data. * parameters_len: Length of the parameters string. * target: The kind of queryables that should be target of this query. * consolidation: The kind of consolidation that should be applied on replies. * value: The payload of the query. * callback: The callback function that will be called on reception of replies for this query. * dropper: The callback function that will be called on upon completion of the callback. * arg: A pointer that will be passed to the **callback** on each call. * timeout_ms: The timeout value of this query. * attachment: An optional attachment to this query. * qos: QoS to apply when routing this query. * source_info: Querier source info. * accept_replies: The accepted replies for this query. * allowed_destination: Locality restrictions for delivery. * opt_cancellation_token: Optional cancellation token to cancel the query, can be null. * */ z_result_t _z_query(const _z_session_rc_t *session, _z_optional_id_t querier_id, const _z_declared_keyexpr_t *keyexpr, const char *parameters, size_t parameters_len, z_query_target_t target, z_consolidation_mode_t consolidation, _z_bytes_t *payload, _z_encoding_t *encoding, _z_closure_reply_callback_t callback, _z_drop_handler_t dropper, void *arg, uint64_t timeout_ms, _z_bytes_t *attachment, _z_n_qos_t qos, _z_source_info_t *source_info, z_reply_keyexpr_t accept_replies, z_locality_t allowed_destination, _z_cancellation_token_rc_t *opt_cancellation_token); #endif #if Z_FEATURE_INTEREST == 1 uint32_t _z_add_interest(_z_session_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_interest_handler_t callback, uint8_t flags, _z_void_rc_t *arg); z_result_t _z_remove_interest(_z_session_t *zn, uint32_t interest_id); #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_NET_PRIMITIVES_H */ ================================================ FILE: include/zenoh-pico/net/publish.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_NET_PUBLISH_H #define INCLUDE_ZENOH_PICO_NET_PUBLISH_H #include "zenoh-pico/api/constants.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif /** * Return type when declaring a publisher. */ typedef struct _z_publisher_t { _z_declared_keyexpr_t _key; _z_zint_t _id; _z_session_weak_t _zn; _z_encoding_t _encoding; z_congestion_control_t _congestion_control; z_priority_t _priority; z_reliability_t reliability; bool _is_express; z_locality_t _allowed_destination; _z_write_filter_t _filter; } _z_publisher_t; #if Z_FEATURE_PUBLICATION == 1 // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_publisher_t _z_publisher_null(void) { return (_z_publisher_t){0}; } static inline bool _z_publisher_check(const _z_publisher_t *publisher) { return !_Z_RC_IS_NULL(&publisher->_zn); } #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_NET_PUBLISH_H */ ================================================ FILE: include/zenoh-pico/net/query.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_QUERY_NETAPI_H #define ZENOH_PICO_QUERY_NETAPI_H #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/utils/query_params.h" #ifdef __cplusplus extern "C" { #endif /** * The query to be answered by a queryable. */ typedef struct _z_query_t { _z_declared_keyexpr_t _key; _z_value_t _value; uint32_t _request_id; _z_session_weak_t _zn; _z_bytes_t _attachment; _z_string_t _parameters; _z_source_info_t _source_info; _z_n_qos_t _qos; bool _anyke; bool _is_local; } _z_query_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_query_t _z_query_null(void) { return (_z_query_t){0}; } static inline bool _z_query_check(const _z_query_t *query) { return _z_declared_keyexpr_check(&query->_key) || _z_value_check(&query->_value) || _z_bytes_check(&query->_attachment) || _z_string_check(&query->_parameters); } z_result_t _z_query_send_reply_final(_z_query_t *q); z_result_t _z_session_send_reply_final(_z_session_t *session, uint32_t query_id, bool is_local); void _z_query_clear(_z_query_t *q); void _z_query_free(_z_query_t **query); _Z_REFCOUNT_DEFINE(_z_query, _z_query) /** * Return type when declaring a querier. */ typedef struct _z_querier_t { _z_declared_keyexpr_t _key; uint32_t _id; _z_session_weak_t _zn; _z_encoding_t _encoding; z_consolidation_mode_t _consolidation_mode; z_query_target_t _target; z_congestion_control_t _congestion_control; z_priority_t _priority; z_reliability_t reliability; bool _is_express; z_reply_keyexpr_t _accept_replies; uint64_t _timeout_ms; z_locality_t _allowed_destination; _z_write_filter_t _filter; } _z_querier_t; #if Z_FEATURE_QUERY == 1 // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_querier_t _z_querier_null(void) { return (_z_querier_t){0}; } static inline bool _z_querier_check(const _z_querier_t *querier) { return !_Z_RC_IS_NULL(&querier->_zn); } #endif /** * Return type when declaring a queryable. */ typedef struct { uint32_t _entity_id; _z_session_weak_t _zn; _z_sync_group_t _callback_drop_sync_group; } _z_queryable_t; #if Z_FEATURE_QUERYABLE == 1 // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_queryable_t _z_queryable_null(void) { return (_z_queryable_t){0}; } static inline bool _z_queryable_check(const _z_queryable_t *queryable) { return !_Z_RC_IS_NULL(&queryable->_zn); } static inline z_result_t _z_query_move_data(_z_query_t *dst, _z_value_t *value, _z_keyexpr_t *key, _z_slice_t *parameters, const _z_session_weak_t *zn, uint32_t request_id, _z_bytes_t *attachment, bool implicit_anyke, const _z_source_info_t *source_info) { *dst = _z_query_null(); dst->_anyke = implicit_anyke || _z_parameters_has_anyke((const char *)parameters->start, parameters->len); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_move(&dst->_key._inner, key), _z_query_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_value_move(&dst->_value, value), _z_query_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_move(&dst->_attachment, attachment), _z_query_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_slice_move(&dst->_parameters._slice, parameters), _z_query_clear(dst)); dst->_request_id = request_id; dst->_zn = _z_session_weak_clone(zn); dst->_source_info = *source_info; return _Z_RES_OK; } void _z_queryable_clear(_z_queryable_t *qbl); void _z_queryable_free(_z_queryable_t **qbl); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_QUERY_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/reply.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_REPLY_NETAPI_H #define ZENOH_PICO_REPLY_NETAPI_H #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif /** * Reply tag values. * * Enumerators: * _Z_REPLY_TAG_DATA: Tag identifying that the reply contains some data. * _Z_REPLY_TAG_FINAL: Tag identifying that the reply does not contain any data and that there will be no more * replies for this query. * _Z_REPLY_TAG_ERROR: Tag identifying that the reply contains error. * _Z_REPLY_TAG_NONE: Tag identifying empty reply. */ typedef enum { _Z_REPLY_TAG_DATA = 0, _Z_REPLY_TAG_FINAL = 1, _Z_REPLY_TAG_ERROR = 2, _Z_REPLY_TAG_NONE = 3 } _z_reply_tag_t; /** * An reply to a :c:func:`z_query`. * * Members: * _z_sample_t data: a :c:type:`_z_sample_t` containing the key and value of the reply. * _z_slice_t replier_id: The id of the entity that sent this reply. * */ typedef struct _z_reply_data_t { union { _z_value_t error; _z_sample_t sample; } _result; _z_entity_global_id_t replier_id; _z_reply_tag_t _tag; } _z_reply_data_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_reply_data_t _z_reply_data_null(void) { return (_z_reply_data_t){0}; } void _z_reply_data_clear(_z_reply_data_t *rd); z_result_t _z_reply_data_copy(_z_reply_data_t *dst, const _z_reply_data_t *src); /** * An reply to a :c:func:`z_query`. * * Members: * _z_reply_t_Tag tag: Indicates if the reply contains data or if it's a FINAL reply. * _z_reply_data_t data: The reply data if :c:member:`_z_reply_t.tag` equals * :c:member:`_z_reply_t_Tag._Z_REPLY_TAG_DATA`. * */ typedef struct _z_reply_t { _z_reply_data_t data; } _z_reply_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_reply_t _z_reply_null(void) { return (_z_reply_t){0}; } void _z_reply_steal_data(_z_reply_t *dst, _z_keyexpr_t *keyexpr, _z_entity_global_id_t replier_id, _z_bytes_t *payload, const _z_timestamp_t *timestamp, _z_encoding_t *encoding, z_sample_kind_t kind, _z_bytes_t *attachment, _z_source_info_t *source_info); void _z_reply_err_steal_data(_z_reply_t *dst, _z_bytes_t *payload, _z_encoding_t *encoding, _z_entity_global_id_t replier_id); z_result_t _z_reply_move(_z_reply_t *dst, _z_reply_t *src); void _z_reply_clear(_z_reply_t *src); void _z_reply_free(_z_reply_t **hello); z_result_t _z_reply_copy(_z_reply_t *dst, const _z_reply_t *src); typedef struct _z_pending_reply_t { _z_reply_t _reply; _z_timestamp_t _tstamp; } _z_pending_reply_t; bool _z_pending_reply_eq(const _z_pending_reply_t *one, const _z_pending_reply_t *two); void _z_pending_reply_clear(_z_pending_reply_t *res); _Z_ELEM_DEFINE(_z_pending_reply, _z_pending_reply_t, _z_noop_size, _z_pending_reply_clear, _z_noop_copy, _z_noop_move, _z_pending_reply_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_pending_reply, _z_pending_reply_t, false) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_REPLY_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/sample.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_SAMPLE_NETAPI_H #define ZENOH_PICO_SAMPLE_NETAPI_H #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/ring.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif /** * A zenoh-net data sample. * * A sample is the value associated to a given resource at a given point in time. * * Members: * _z_keyexpr_t key: The resource key of this data sample. * _z_slice_t value: The value of this data sample. * _z_encoding_t encoding: The encoding for the value of this data sample. * _z_source_info_t source_info: The source info for this data sample (unstable). */ typedef struct _z_sample_t { _z_declared_keyexpr_t keyexpr; _z_bytes_t payload; _z_timestamp_t timestamp; _z_encoding_t encoding; z_sample_kind_t kind; _z_qos_t qos; _z_bytes_t attachment; z_reliability_t reliability; _z_source_info_t source_info; } _z_sample_t; void _z_sample_clear(_z_sample_t *sample); // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_sample_t _z_sample_null(void) { return (_z_sample_t){0}; } static inline bool _z_sample_check(const _z_sample_t *sample) { return _z_declared_keyexpr_check(&sample->keyexpr) || _z_encoding_check(&sample->encoding) || _z_bytes_check(&sample->payload) || _z_bytes_check(&sample->attachment); } static inline size_t _z_sample_size(const _z_sample_t *s) { (void)(s); return sizeof(_z_sample_t); } void _z_sample_steal_data(_z_sample_t *dst, _z_keyexpr_t *key, _z_bytes_t *payload, const _z_timestamp_t *timestamp, _z_encoding_t *encoding, z_sample_kind_t kind, _z_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info); z_result_t _z_sample_copy_data(_z_sample_t *dst, const _z_declared_keyexpr_t *key, const _z_bytes_t *payload, const _z_timestamp_t *timestamp, const _z_encoding_t *encoding, z_sample_kind_t kind, _z_qos_t qos, const _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info); z_result_t _z_sample_move(_z_sample_t *dst, _z_sample_t *src); /** * Free a :c:type:`_z_sample_t`, including its internal fields. * * Parameters: * sample: The :c:type:`_z_sample_t` to free. */ void _z_sample_free(_z_sample_t **sample); z_result_t _z_sample_copy(_z_sample_t *dst, const _z_sample_t *src); _z_sample_t _z_sample_duplicate(const _z_sample_t *src); _Z_ELEM_DEFINE(_z_sample, _z_sample_t, _z_sample_size, _z_sample_clear, _z_sample_copy, _z_sample_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_RING_DEFINE(_z_sample, _z_sample_t) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SAMPLE_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/session.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #ifndef INCLUDE_ZENOH_PICO_NET_SESSION_H #define INCLUDE_ZENOH_PICO_NET_SESSION_H #include #include "zenoh-pico/collections/atomic.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/matching.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/utils/config.h" #ifdef __cplusplus extern "C" { #endif /** * A zenoh-net session. */ struct _z_write_filter_registration_t; #if Z_FEATURE_CONNECTIVITY == 1 typedef struct { _z_void_rc_t _callback; } _z_connectivity_transport_listener_t; static inline size_t _z_connectivity_transport_listener_size(const _z_connectivity_transport_listener_t *listener) { _ZP_UNUSED(listener); return sizeof(_z_connectivity_transport_listener_t); } static inline void _z_connectivity_transport_listener_clear(_z_connectivity_transport_listener_t *listener) { _z_void_rc_drop(&listener->_callback); listener->_callback = _z_void_rc_null(); } static inline void _z_connectivity_transport_listener_copy(_z_connectivity_transport_listener_t *dst, const _z_connectivity_transport_listener_t *src) { dst->_callback = _z_void_rc_clone(&src->_callback); } static inline void _z_connectivity_transport_listener_move(_z_connectivity_transport_listener_t *dst, _z_connectivity_transport_listener_t *src) { dst->_callback = src->_callback; src->_callback = _z_void_rc_null(); } _Z_ELEM_DEFINE(_z_connectivity_transport_listener, _z_connectivity_transport_listener_t, _z_connectivity_transport_listener_size, _z_connectivity_transport_listener_clear, _z_connectivity_transport_listener_copy, _z_connectivity_transport_listener_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_connectivity_transport_listener, _z_connectivity_transport_listener_t) typedef struct { _z_void_rc_t _callback; bool _has_transport_filter; _z_id_t _transport_zid; bool _transport_is_multicast; } _z_connectivity_link_listener_t; static inline size_t _z_connectivity_link_listener_size(const _z_connectivity_link_listener_t *listener) { _ZP_UNUSED(listener); return sizeof(_z_connectivity_link_listener_t); } static inline void _z_connectivity_link_listener_clear(_z_connectivity_link_listener_t *listener) { _z_void_rc_drop(&listener->_callback); listener->_callback = _z_void_rc_null(); listener->_has_transport_filter = false; listener->_transport_zid = (_z_id_t){0}; listener->_transport_is_multicast = false; } static inline void _z_connectivity_link_listener_copy(_z_connectivity_link_listener_t *dst, const _z_connectivity_link_listener_t *src) { dst->_callback = _z_void_rc_clone(&src->_callback); dst->_has_transport_filter = src->_has_transport_filter; dst->_transport_zid = src->_transport_zid; dst->_transport_is_multicast = src->_transport_is_multicast; } static inline void _z_connectivity_link_listener_move(_z_connectivity_link_listener_t *dst, _z_connectivity_link_listener_t *src) { dst->_callback = src->_callback; dst->_has_transport_filter = src->_has_transport_filter; dst->_transport_zid = src->_transport_zid; dst->_transport_is_multicast = src->_transport_is_multicast; src->_callback = _z_void_rc_null(); src->_has_transport_filter = false; src->_transport_zid = (_z_id_t){0}; src->_transport_is_multicast = false; } _Z_ELEM_DEFINE(_z_connectivity_link_listener, _z_connectivity_link_listener_t, _z_connectivity_link_listener_size, _z_connectivity_link_listener_clear, _z_connectivity_link_listener_copy, _z_connectivity_link_listener_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_connectivity_link_listener, _z_connectivity_link_listener_t) #endif typedef struct _z_session_t { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex_inner; _z_mutex_rec_t _mutex_transport; #if Z_FEATURE_ADMIN_SPACE == 1 _z_mutex_t _mutex_admin_space; #endif #endif // Z_FEATURE_MULTI_THREAD == 1 // Zenoh-pico is considering a single transport per session. z_whatami_t _mode; _z_transport_t _tp; // Zenoh PID _z_id_t _local_zid; // Session counters uint16_t _resource_id; uint32_t _entity_id; _z_zint_t _query_id; _z_zint_t _interest_id; // Session declarations _z_resource_slist_t *_local_resources; // Information for session restoring and asynchronous peer connection _z_config_t _config; #if Z_FEATURE_AUTO_RECONNECT == 1 _z_network_message_slist_t *_declaration_cache; #endif // Session subscriptions #if Z_FEATURE_SUBSCRIPTION == 1 _z_subscription_rc_slist_t *_subscriptions; _z_subscription_rc_slist_t *_liveliness_subscriptions; #if Z_FEATURE_RX_CACHE == 1 _z_subscription_lru_cache_t _subscription_cache; #endif #endif #if Z_FEATURE_LIVELINESS == 1 _z_declared_keyexpr_intmap_t _local_tokens; _z_keyexpr_intmap_t _remote_tokens; #if Z_FEATURE_QUERY == 1 uint32_t _liveliness_query_id; _z_liveliness_pending_query_intmap_t _liveliness_pending_queries; #endif #endif // Session queryables #if Z_FEATURE_QUERYABLE == 1 _z_session_queryable_rc_slist_t *_local_queryable; #if Z_FEATURE_RX_CACHE == 1 _z_queryable_lru_cache_t _queryable_cache; #endif #endif #if Z_FEATURE_QUERY == 1 _z_pending_query_slist_t *_pending_queries; #endif // Session interests #if Z_FEATURE_INTEREST == 1 _z_session_interest_rc_slist_t *_local_interests; _z_declare_data_slist_t *_remote_declares; struct _z_write_filter_registration_t *_write_filters; #endif #if Z_FEATURE_ADMIN_SPACE == 1 // entity Id for admin space queryable (0 if not started) uint32_t _admin_space_queryable_id; #if Z_FEATURE_CONNECTIVITY == 1 // entity Id for session connectivity queryable (0 if not started) uint32_t _admin_space_session_queryable_id; #if Z_FEATURE_PUBLICATION == 1 // listener ids for admin-space connectivity event bridge (0 if not started) size_t _admin_space_transport_listener_id; size_t _admin_space_link_listener_id; #endif #endif #endif #if Z_FEATURE_CONNECTIVITY == 1 size_t _connectivity_next_listener_id; _z_connectivity_transport_listener_intmap_t _connectivity_transport_event_listeners; _z_connectivity_link_listener_intmap_t _connectivity_link_event_listeners; #endif _z_sync_group_t _callback_drop_sync_group; _z_atomic_bool_t _is_closed; _z_runtime_t _runtime; } _z_session_t; /** * Open a zenoh-net session * * Parameters: * zn: A pointer of A :c:type:`_z_session_t` used as a return value. * config: A set of properties. The caller keeps its ownership. * zid: A pointer to Zenoh ID. * * Returns: * ``0`` in case of success, or a ``negative value`` in case of failure. */ z_result_t _z_open(_z_session_rc_t *zn, _z_config_t *config, const _z_id_t *zid); #if Z_FEATURE_AUTO_RECONNECT == 1 void _z_client_reopen_task_drop(void *ztc_arg); _z_fut_fn_result_t _z_client_reopen_task_fn(void *ztc_arg, _z_executor_t *executor); #endif /** * Store declaration network message to cache for resend it after session restore * * Parameters: * zs: A zenoh-net session. * z_msg: Network message with declaration */ void _z_cache_declaration(_z_session_t *zs, const _z_network_message_t *n_msg); /** * Remove corresponding declaration from the cache * * Parameters: * zs: A zenoh-net session. * z_msg: Network message with undeclaration */ void _z_prune_declaration(_z_session_t *zs, const _z_network_message_t *n_msg); /** * Return true is session and all associated transports were closed. */ bool _z_session_is_closed(const _z_session_t *session); /** * Return true if session is connected to at least one router peer. */ bool _z_session_has_router_peer(const _z_session_t *session); /** * Upgrade a weak session reference to a strong one if the session is open, otherwise return null. */ _z_session_rc_t _z_session_weak_upgrade_if_open(const _z_session_weak_t *weak); /** * Get informations about an zenoh-net session. * * Parameters: * session: A zenoh-net session. The caller keeps its ownership. * * Returns: * A :c:type:`_z_config_t` map containing informations on the given zenoh-net session. */ _z_config_t *_z_info(const _z_session_t *session); /*------------------ Zenoh-Pico Session Management Auxiliary ------------------*/ /** * Read from the network. This function should be called manually called when * the read loop has not been started, e.g., when running in a single thread. * * Parameters: * session: The zenoh-net session. The caller keeps its ownership. * single_read: Read a single packet from the buffer instead of the whole buffer * Returns: * ``0`` in case of success, ``-1`` in case of failure. */ z_result_t _zp_read(_z_session_t *z, bool single_read); /** * Send a KeepAlive message. * * Parameters: * session: The zenoh-net session. The caller keeps its ownership. * Returns: * ``0`` in case of success, ``-1`` in case of failure. */ z_result_t _zp_send_keep_alive(_z_session_t *z); /** * Send a Join message. * * Parameters: * session: The zenoh-net session. The caller keeps its ownership. * Returns: * ``0`` in case of success, ``-1`` in case of failure. */ z_result_t _zp_send_join(_z_session_t *z); z_result_t _zp_start_transport_tasks(_z_session_t *z); #if Z_FEATURE_CONNECTIVITY == 1 void _z_connectivity_peer_connected(_z_session_t *session, const _z_connectivity_peer_event_data_t *peer, bool is_multicast, uint16_t mtu, bool is_streamed, bool is_reliable); void _z_connectivity_peer_disconnected(_z_session_t *session, const _z_connectivity_peer_event_data_t *peer, bool is_multicast, uint16_t mtu, bool is_streamed, bool is_reliable); void _z_connectivity_peer_disconnected_from_transport(_z_session_t *session, const _z_transport_common_t *transport, const _z_connectivity_peer_event_data_t *peer, bool is_multicast); #endif static inline _z_session_t *_z_transport_common_get_session(_z_transport_common_t *transport) { // the session should always outlive the transport, so it should be safe // to access pointer directly without upgrade return _z_session_weak_as_unsafe_ptr(&transport->_session); } #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_NET_SESSION_H */ ================================================ FILE: include/zenoh-pico/net/subscribe.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SUBSCRIBE_NETAPI_H #define ZENOH_PICO_SUBSCRIBE_NETAPI_H #include #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif /** * Return type when declaring a subscriber. */ typedef struct { uint32_t _entity_id; _z_session_weak_t _zn; _z_sync_group_t _callback_drop_sync_group; } _z_subscriber_t; #if Z_FEATURE_SUBSCRIPTION == 1 // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_subscriber_t _z_subscriber_null(void) { return (_z_subscriber_t){0}; } static inline bool _z_subscriber_check(const _z_subscriber_t *subscriber) { return !_Z_RC_IS_NULL(&subscriber->_zn); } void _z_subscriber_clear(_z_subscriber_t *sub); void _z_subscriber_free(_z_subscriber_t **sub); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SUBSCRIBE_NETAPI_H */ ================================================ FILE: include/zenoh-pico/net/zenoh-pico.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef ZENOH_PICO_NET_H #define ZENOH_PICO_NET_H #include "zenoh-pico/net/config.h" #include "zenoh-pico/net/logger.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/net/publish.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/net/subscribe.h" #if defined(ZENOH_ZEPHYR) #include #endif #endif /* ZENOH_PICO_NET_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/core.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_CORE_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_CORE_H #include #include #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif typedef z_result_t (*__z_single_byte_reader_t)(uint8_t *, void *context); /*------------------ Internal Zenoh-net Macros ------------------*/ z_result_t _z_consolidation_mode_encode(_z_wbuf_t *wbf, z_consolidation_mode_t en); z_result_t _z_consolidation_mode_decode(z_consolidation_mode_t *en, _z_zbuf_t *zbf); z_result_t _z_query_target_encode(_z_wbuf_t *wbf, z_query_target_t en); z_result_t _z_query_target_decode(z_query_target_t *en, _z_zbuf_t *zbf); z_result_t _z_whatami_encode(_z_wbuf_t *wbf, z_whatami_t en); z_result_t _z_whatami_decode(z_whatami_t *en, _z_zbuf_t *zbf); uint8_t _z_whatami_to_uint8(z_whatami_t whatami); z_whatami_t _z_whatami_from_uint8(uint8_t b); z_result_t _z_uint8_encode(_z_wbuf_t *buf, uint8_t v); z_result_t _z_uint8_decode(uint8_t *u8, _z_zbuf_t *buf); z_result_t _z_uint8_decode_as_ref(uint8_t **u8, _z_zbuf_t *zbf); z_result_t _z_uint16_encode(_z_wbuf_t *buf, uint16_t v); z_result_t _z_uint16_decode(uint16_t *u16, _z_zbuf_t *buf); uint8_t _z_zint_len(uint64_t v); uint8_t _z_zint64_encode_buf(uint8_t *buf, uint64_t v); static inline uint8_t _z_zsize_encode_buf(uint8_t *buf, _z_zint_t v) { return _z_zint64_encode_buf(buf, (uint64_t)v); } z_result_t _z_zint64_encode(_z_wbuf_t *buf, uint64_t v); static inline z_result_t _z_zint16_encode(_z_wbuf_t *wbf, uint16_t v) { return _z_zint64_encode(wbf, (uint64_t)v); } static inline z_result_t _z_zint32_encode(_z_wbuf_t *wbf, uint32_t v) { return _z_zint64_encode(wbf, (uint64_t)v); } static inline z_result_t _z_zsize_encode(_z_wbuf_t *wbf, _z_zint_t v) { return _z_zint64_encode(wbf, (uint64_t)v); } z_result_t _z_zint16_decode(uint16_t *zint, _z_zbuf_t *buf); z_result_t _z_zint32_decode(uint32_t *zint, _z_zbuf_t *buf); z_result_t _z_zint64_decode(uint64_t *zint, _z_zbuf_t *buf); z_result_t _z_zint64_decode_with_reader(uint64_t *zint, __z_single_byte_reader_t reader, void *context); z_result_t _z_zsize_decode_with_reader(_z_zint_t *zint, __z_single_byte_reader_t reader, void *context); z_result_t _z_zsize_decode(_z_zint_t *zint, _z_zbuf_t *buf); z_result_t _z_buf_encode(_z_wbuf_t *wbf, const uint8_t *buf, size_t len); static inline z_result_t _z_slice_val_encode(_z_wbuf_t *wbf, const _z_slice_t *bs) { return _z_buf_encode(wbf, bs->start, bs->len); } static inline z_result_t _z_slice_val_decode_na(_z_slice_t *bs, _z_zbuf_t *zbf) { // Check if we have enough bytes to read if (_z_zbuf_len(zbf) < bs->len) { _Z_WARN("Not enough bytes to read"); bs->len = 0; bs->start = NULL; _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *bs = _z_slice_alias_buf(_z_zbuf_get_rptr(zbf), bs->len); // Decode without allocating _z_zbuf_set_rpos(zbf, _z_zbuf_get_rpos(zbf) + bs->len); // Move the read position return _Z_RES_OK; } static inline z_result_t _z_slice_decode_na(_z_slice_t *bs, _z_zbuf_t *zbf) { _Z_RETURN_IF_ERR(_z_zsize_decode(&bs->len, zbf)); return _z_slice_val_decode_na(bs, zbf); } static inline z_result_t _z_slice_val_decode(_z_slice_t *bs, _z_zbuf_t *zbf) { return _z_slice_val_decode_na(bs, zbf); } static inline z_result_t _z_slice_decode(_z_slice_t *bs, _z_zbuf_t *zbf) { return _z_slice_decode_na(bs, zbf); } z_result_t _z_slice_encode(_z_wbuf_t *buf, const _z_slice_t *bs); z_result_t _z_slices_encode(_z_wbuf_t *wbf, const _z_slice_t *bs, size_t num_slices); z_result_t _z_bytes_decode(_z_bytes_t *bs, _z_zbuf_t *zbf, _z_arc_slice_t *arcs); z_result_t _z_bytes_encode(_z_wbuf_t *wbf, const _z_bytes_t *bs); z_result_t _z_zbuf_read_exact(_z_zbuf_t *zbf, uint8_t *dest, size_t length); z_result_t _z_str_encode(_z_wbuf_t *buf, const char *s); z_result_t _z_str_decode(char **str, _z_zbuf_t *buf); z_result_t _z_string_encode(_z_wbuf_t *wbf, const _z_string_t *s); z_result_t _z_string_decode(_z_string_t *str, _z_zbuf_t *zbf); size_t _z_encoding_len(const _z_encoding_t *en); z_result_t _z_encoding_encode(_z_wbuf_t *wbf, const _z_encoding_t *en); z_result_t _z_encoding_decode(_z_encoding_t *en, _z_zbuf_t *zbf); z_result_t _z_value_encode(_z_wbuf_t *wbf, const _z_value_t *en); z_result_t _z_value_decode(_z_value_t *en, _z_zbuf_t *zbf); z_result_t _z_wireexpr_encode(_z_wbuf_t *buf, bool has_suffix, const _z_wireexpr_t *ke); z_result_t _z_wireexpr_decode(_z_wireexpr_t *ke, _z_zbuf_t *buf, bool has_suffix, bool remote_mapping, uintptr_t mapping); z_result_t _z_timestamp_encode(_z_wbuf_t *buf, const _z_timestamp_t *ts); z_result_t _z_timestamp_encode_ext(_z_wbuf_t *buf, const _z_timestamp_t *ts); z_result_t _z_timestamp_decode(_z_timestamp_t *ts, _z_zbuf_t *buf); z_result_t _z_source_info_encode(_z_wbuf_t *wbf, const _z_source_info_t *info); z_result_t _z_source_info_encode_ext(_z_wbuf_t *wbf, const _z_source_info_t *info); z_result_t _z_source_info_decode(_z_source_info_t *info, _z_zbuf_t *zbf); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_CORE_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/declarations.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_DECLARATIONS_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_DECLARATIONS_H #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif #define _Z_DECL_KEXPR_MID 0 #define _Z_DECL_KEXPR_FLAG_N 0x20 #define _Z_UNDECL_KEXPR_MID 1 #define _Z_DECL_SUBSCRIBER_MID 2 #define _Z_DECL_SUBSCRIBER_FLAG_N 0x20 #define _Z_DECL_SUBSCRIBER_FLAG_M 0x40 #define _Z_UNDECL_SUBSCRIBER_MID 3 #define _Z_DECL_QUERYABLE_MID 4 #define _Z_UNDECL_QUERYABLE_MID 5 #define _Z_DECL_TOKEN_MID 6 #define _Z_UNDECL_TOKEN_MID 7 #define _Z_DECL_FINAL_MID 0x1a z_result_t _z_decl_kexpr_encode(_z_wbuf_t *wbf, const _z_decl_kexpr_t *decl); z_result_t _z_decl_kexpr_decode(_z_decl_kexpr_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_undecl_kexpr_encode(_z_wbuf_t *wbf, const _z_undecl_kexpr_t *decl); z_result_t _z_undecl_kexpr_decode(_z_undecl_kexpr_t *decl, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_decl_subscriber_encode(_z_wbuf_t *wbf, const _z_decl_subscriber_t *decl); z_result_t _z_decl_subscriber_decode(_z_decl_subscriber_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_undecl_subscriber_encode(_z_wbuf_t *wbf, const _z_undecl_subscriber_t *decl); z_result_t _z_undecl_subscriber_decode(_z_undecl_subscriber_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_decl_queryable_encode(_z_wbuf_t *wbf, const _z_decl_queryable_t *decl); z_result_t _z_decl_queryable_decode(_z_decl_queryable_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_undecl_queryable_encode(_z_wbuf_t *wbf, const _z_undecl_queryable_t *decl); z_result_t _z_undecl_queryable_decode(_z_undecl_queryable_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_decl_token_encode(_z_wbuf_t *wbf, const _z_decl_token_t *decl); z_result_t _z_decl_token_decode(_z_decl_token_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_undecl_token_encode(_z_wbuf_t *wbf, const _z_undecl_token_t *decl); z_result_t _z_undecl_token_decode(_z_undecl_token_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_declaration_encode(_z_wbuf_t *wbf, const _z_declaration_t *decl); z_result_t _z_declaration_decode(_z_declaration_t *decl, _z_zbuf_t *zbf, uintptr_t mapping); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_DECLARATIONS_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/ext.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_EXTCODEC_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_EXTCODEC_H #include #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif /*------------------ Message Extension ------------------*/ z_result_t _z_msg_ext_encode(_z_wbuf_t *wbf, const _z_msg_ext_t *ext, bool has_next); z_result_t _z_msg_ext_decode(_z_msg_ext_t *ext, _z_zbuf_t *zbf, bool *has_next); z_result_t _z_msg_ext_decode_na(_z_msg_ext_t *ext, _z_zbuf_t *zbf, bool *has_next); z_result_t _z_msg_ext_vec_encode(_z_wbuf_t *wbf, const _z_msg_ext_vec_t *extensions); z_result_t _z_msg_ext_vec_decode(_z_msg_ext_vec_t *extensions, _z_zbuf_t *zbf); /** * Iterates through the extensions in `zbf`, assuming at least one is present at its beginning * (calling this function otherwise is UB). Short-circuits if `callback` returns a non-zero value. * * `callback` will receive `context` as its second argument, and may "steal" its first argument by * copying its value and setting it to `_z_msg_ext_make_unit(0)`. */ z_result_t _z_msg_ext_decode_iter(_z_zbuf_t *zbf, z_result_t (*callback)(_z_msg_ext_t *, void *), void *context); /** * Iterates through the extensions in `zbf`, assuming at least one is present at its beginning. * Returns `_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN` if a mandatory extension is found, * `_Z_RES_OK` otherwise. */ z_result_t _z_msg_ext_skip_non_mandatories(_z_zbuf_t *zbf, uint8_t trace_id); /** * Logs an error to debug the unknown extension, returning `_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN`. * * `trace_id` may be any arbitrary value, but is advised to be unique to its call-site, * to help debugging should it be necessary. */ z_result_t _z_msg_ext_unknown_error(_z_msg_ext_t *extension, uint8_t trace_id); #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_EXTCODEC_H */ // NOTE: the following headers are for unit testing only #ifdef ZENOH_PICO_TEST_H // ------------------ Message Fields ------------------ z_result_t _z_msg_ext_encode_unit(_z_wbuf_t *wbf, const _z_msg_ext_unit_t *pld); z_result_t _z_msg_ext_decode_unit(_z_msg_ext_unit_t *pld, _z_zbuf_t *zbf); z_result_t _z_msg_ext_decode_unit_na(_z_msg_ext_unit_t *pld, _z_zbuf_t *zbf); z_result_t _z_msg_ext_encode_zint(_z_wbuf_t *wbf, const _z_msg_ext_zint_t *pld); z_result_t _z_msg_ext_decode_zint(_z_msg_ext_zint_t *pld, _z_zbuf_t *zbf); z_result_t _z_msg_ext_decode_zint_na(_z_msg_ext_zint_t *pld, _z_zbuf_t *zbf); z_result_t _z_msg_ext_encode_zbuf(_z_wbuf_t *wbf, const _z_msg_ext_zbuf_t *pld); z_result_t _z_msg_ext_decode_zbuf(_z_msg_ext_zbuf_t *pld, _z_zbuf_t *zbf); z_result_t _z_msg_ext_decode_zbuf_na(_z_msg_ext_zbuf_t *pld, _z_zbuf_t *zbf); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TEST_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/interest.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_INTEREST_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_INTEREST_H #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_interest_encode(_z_wbuf_t *wbf, const _z_interest_t *interest, bool is_final); z_result_t _z_interest_decode(_z_interest_t *decl, _z_zbuf_t *zbf, bool is_final, bool has_ext, uintptr_t mapping); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_DECLARATIONS_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/message.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_MESSAGE_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_MESSAGE_H #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_push_body_encode(_z_wbuf_t *wbf, const _z_push_body_t *pshb); z_result_t _z_push_body_decode(_z_push_body_t *body, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs); z_result_t _z_query_encode(_z_wbuf_t *wbf, const _z_msg_query_t *query); z_result_t _z_query_decode(_z_msg_query_t *query, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_reply_encode(_z_wbuf_t *wbf, const _z_msg_reply_t *reply); z_result_t _z_reply_decode(_z_msg_reply_t *reply, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs); z_result_t _z_err_encode(_z_wbuf_t *wbf, const _z_msg_err_t *err); z_result_t _z_err_decode(_z_msg_err_t *err, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs); z_result_t _z_put_encode(_z_wbuf_t *wbf, const _z_msg_put_t *put); z_result_t _z_put_decode(_z_msg_put_t *put, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs); z_result_t _z_del_encode(_z_wbuf_t *wbf, const _z_msg_del_t *del); z_result_t _z_del_decode(_z_msg_del_t *del, _z_zbuf_t *zbf, uint8_t header); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_MESSAGE_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/network.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_NETWORK_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_NETWORK_H #include #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_push_encode(_z_wbuf_t *wbf, const _z_n_msg_push_t *msg); z_result_t _z_push_decode(_z_n_msg_push_t *msg, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping); z_result_t _z_request_encode(_z_wbuf_t *wbf, const _z_n_msg_request_t *msg); z_result_t _z_request_decode(_z_n_msg_request_t *msg, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping); z_result_t _z_response_encode(_z_wbuf_t *wbf, const _z_n_msg_response_t *msg); z_result_t _z_response_decode(_z_n_msg_response_t *msg, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping); z_result_t _z_response_final_encode(_z_wbuf_t *wbf, const _z_n_msg_response_final_t *msg); z_result_t _z_response_final_decode(_z_n_msg_response_final_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_declare_encode(_z_wbuf_t *wbf, const _z_n_msg_declare_t *decl); z_result_t _z_declare_decode(_z_n_msg_declare_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_n_interest_encode(_z_wbuf_t *wbf, const _z_n_msg_interest_t *interest); z_result_t _z_n_interest_decode(_z_n_msg_interest_t *interest, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping); z_result_t _z_oam_encode(_z_wbuf_t *wbf, const _z_n_msg_oam_t *oam); z_result_t _z_oam_decode(_z_n_msg_oam_t *oam, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_network_message_encode(_z_wbuf_t *wbf, const _z_network_message_t *msg); z_result_t _z_network_message_decode(_z_network_message_t *msg, _z_zbuf_t *zbf, _z_arc_slice_t *arcs, uintptr_t mapping); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_NETWORK_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/serial.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_SERIAL_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_SERIAL_H #include #ifdef __cplusplus extern "C" { #endif size_t _z_serial_msg_serialize(uint8_t *dest, size_t dest_len, const uint8_t *src, size_t src_len, uint8_t header, uint8_t *tmp_buf, size_t tmp_buf_len); size_t _z_serial_msg_deserialize(const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, uint8_t *header, uint8_t *tmp_buf, size_t tmp_buf_len); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_SERIAL_H */ ================================================ FILE: include/zenoh-pico/protocol/codec/transport.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_TRANSPORT_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_TRANSPORT_H #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/protocol/iobuf.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_scouting_message_encode(_z_wbuf_t *buf, const _z_scouting_message_t *msg); z_result_t _z_scouting_message_decode(_z_scouting_message_t *msg, _z_zbuf_t *buf); z_result_t _z_transport_message_encode(_z_wbuf_t *buf, const _z_transport_message_t *msg); z_result_t _z_transport_message_decode(_z_transport_message_t *msg, _z_zbuf_t *buf); z_result_t _z_join_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_join_t *msg); z_result_t _z_join_decode(_z_t_msg_join_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_init_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_init_t *msg); z_result_t _z_init_decode(_z_t_msg_init_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_open_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_open_t *msg); z_result_t _z_open_decode(_z_t_msg_open_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_close_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_close_t *msg); z_result_t _z_close_decode(_z_t_msg_close_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_keep_alive_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_keep_alive_t *msg); z_result_t _z_keep_alive_decode(_z_t_msg_keep_alive_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_frame_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_frame_t *msg); z_result_t _z_frame_decode(_z_t_msg_frame_t *msg, _z_zbuf_t *zbf, uint8_t header); z_result_t _z_fragment_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_fragment_t *msg); z_result_t _z_fragment_decode(_z_t_msg_fragment_t *msg, _z_zbuf_t *zbf, uint8_t header); #if defined(Z_TEST_HOOKS) typedef z_result_t (*_z_transport_message_encode_override_fn)(_z_wbuf_t *wbf, const _z_transport_message_t *msg, bool *handled); void _z_transport_set_message_encode_override(_z_transport_message_encode_override_fn fn); #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_TRANSPORT_H */ ================================================ FILE: include/zenoh-pico/protocol/codec.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_H #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/message.h" #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CODEC_H */ ================================================ FILE: include/zenoh-pico/protocol/core.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_CORE_H #define INCLUDE_ZENOH_PICO_PROTOCOL_CORE_H #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/string.h" #ifdef __cplusplus extern "C" { #endif #define _Z_OPTIONAL #define _Z_MOVE(x) x * /** * The reserved resource ID indicating a string-only resource key. */ #define Z_RESOURCE_ID_NONE 0 /** * Priority values. */ #define Z_PRIORITIES_NUM 8 /** * A variable-length encoding unsigned integer. */ typedef size_t _z_zint_t; /** * A zenoh ID. */ #define ZENOH_ID_SIZE 16 typedef struct { uint8_t id[ZENOH_ID_SIZE]; } _z_id_t; extern const _z_id_t empty_id; uint8_t _z_id_len(_z_id_t id); static inline bool _z_id_check(_z_id_t id) { return memcmp(&id, &empty_id, sizeof(id)) != 0; } static inline size_t _z_id_size(_z_id_t *id) { _ZP_UNUSED(id); return sizeof(_z_id_t); } static inline void _z_id_copy(_z_id_t *dst, const _z_id_t *src) { size_t offset = 0; (void)_z_memcpy_checked(dst, sizeof(_z_id_t), &offset, src, sizeof(_z_id_t)); } static inline bool _z_id_eq(const _z_id_t *left, const _z_id_t *right) { return memcmp(left->id, right->id, ZENOH_ID_SIZE) == 0; } int _z_id_cmp(const _z_id_t *left, const _z_id_t *right); size_t _z_id_hash(const _z_id_t *id); static inline _z_id_t _z_id_empty(void) { return (_z_id_t){0}; } _Z_ELEM_DEFINE(_z_id, _z_id_t, _z_id_size, _z_noop_clear, _z_id_copy, _z_noop_move, _z_id_eq, _z_id_cmp, _z_id_hash) typedef struct { _z_id_t zid; uint32_t eid; } _z_entity_global_id_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_entity_global_id_t _z_entity_global_id_null(void) { return (_z_entity_global_id_t){0}; } static inline bool _z_entity_global_id_check(const _z_entity_global_id_t *info) { return _z_id_check(info->zid) || info->eid != 0; } static inline size_t _z_entity_global_id_size(_z_entity_global_id_t *id) { _ZP_UNUSED(id); return sizeof(_z_entity_global_id_t); } static inline void _z_entity_global_id_copy(_z_entity_global_id_t *dst, const _z_entity_global_id_t *src) { *dst = *src; } static inline bool _z_entity_global_id_eq(const _z_entity_global_id_t *left, const _z_entity_global_id_t *right) { return memcmp(left, right, sizeof(_z_entity_global_id_t)) == 0; } size_t _z_entity_global_id_hash(const _z_entity_global_id_t *id); _Z_ELEM_DEFINE(_z_entity_global_id, _z_entity_global_id_t, _z_entity_global_id_size, _z_noop_clear, _z_entity_global_id_copy, _z_noop_move, _z_entity_global_id_eq, _z_noop_cmp, _z_entity_global_id_hash) /** * NTP64 time. */ typedef uint64_t _z_ntp64_t; /** * A zenoh timestamp. */ typedef struct { bool valid; _z_id_t id; _z_ntp64_t time; } _z_timestamp_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_timestamp_t _z_timestamp_null(void) { return (_z_timestamp_t){0}; } static inline void _z_timestamp_invalid(_z_timestamp_t *tstamp) { tstamp->valid = false; } static inline bool _z_timestamp_check(const _z_timestamp_t *tstamp) { return tstamp->valid; } void _z_timestamp_copy(_z_timestamp_t *dst, const _z_timestamp_t *src); _z_timestamp_t _z_timestamp_duplicate(const _z_timestamp_t *tstamp); void _z_timestamp_clear(_z_timestamp_t *tstamp); void _z_timestamp_move(_z_timestamp_t *dst, _z_timestamp_t *src); static inline size_t _z_timestamp_size(const _z_timestamp_t *ts) { (void)(ts); return sizeof(_z_timestamp_t); } int _z_timestamp_cmp(const _z_timestamp_t *left, const _z_timestamp_t *right); _z_ntp64_t _z_timestamp_ntp64_from_time(uint32_t seconds, uint32_t nanos); _Z_ELEM_DEFINE(_z_timestamp, _z_timestamp_t, _z_timestamp_size, _z_timestamp_clear, _z_timestamp_copy, _z_noop_move, _z_noop_eq, _z_timestamp_cmp, _z_noop_hash) /** * A zenoh key-expression. * * Members: * uint16_t _id: The resource ID of the ke. * uintptr_t _mapping: Address of the peer as id, if ke is remotely declared. * _z_string_t _suffix: The string value of the ke. */ // Note on the _mapping field: there are collisions on _id value between peers/local, this field is used only to // distinguish which peer/local id space we are in and should not be dereferenced, just compared. NULL/0 value is used // for local declared keyexpr and the address of empty_id as a placeholder. #define _Z_KEYEXPR_MAPPING_LOCAL (uintptr_t)0 typedef struct { uint16_t _id; uintptr_t _mapping; _z_string_t _suffix; } _z_wireexpr_t; static inline void _z_wireexpr_clear(_z_wireexpr_t *expr) { _z_string_clear(&expr->_suffix); } static inline z_result_t _z_wireexpr_copy(_z_wireexpr_t *dst, const _z_wireexpr_t *src) { _Z_RETURN_IF_ERR(_z_string_copy(&dst->_suffix, &src->_suffix)); dst->_id = src->_id; dst->_mapping = src->_mapping; return _Z_RES_OK; } static inline _z_wireexpr_t _z_wireexpr_alias(const _z_wireexpr_t *src) { _z_wireexpr_t dst; dst._suffix = _z_string_alias(src->_suffix); dst._id = src->_id; dst._mapping = src->_mapping; return dst; } static inline _z_wireexpr_t _z_wireexpr_null(void) { _z_wireexpr_t expr = {0}; return expr; } static inline _z_wireexpr_t _z_wireexpr_steal(_z_wireexpr_t *expr) { _z_wireexpr_t expr2 = *expr; *expr = _z_wireexpr_null(); return expr2; } static inline bool _z_wireexpr_is_local(const _z_wireexpr_t *expr) { return expr->_mapping == _Z_KEYEXPR_MAPPING_LOCAL; } static inline bool _z_wireexpr_has_suffix(const _z_wireexpr_t *expr) { return _z_string_check(&expr->_suffix); } static inline bool _z_wireexpr_check(const _z_wireexpr_t *expr) { return _z_string_check(&expr->_suffix) || expr->_id != Z_RESOURCE_ID_NONE; } /** * QoS settings of zenoh message. */ typedef struct { uint8_t _val; } _z_qos_t; /** * Represents a Zenoh value. * * Members: * _z_bytes_t payload: The payload of this zenoh value. * _z_encoding_t encoding: The encoding of the `payload`. */ typedef struct { _z_bytes_t payload; _z_encoding_t encoding; } _z_value_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_value_t _z_value_null(void) { return (_z_value_t){0}; } static inline bool _z_value_check(const _z_value_t *value) { return _z_bytes_check(&value->payload) || _z_encoding_check(&value->encoding); } static inline _z_value_t _z_value_alias(_z_value_t *src) { _z_value_t dst; dst.payload = _z_bytes_alias(&src->payload); dst.encoding = _z_encoding_alias(&src->encoding); return dst; } _z_value_t _z_value_steal(_z_value_t *value); z_result_t _z_value_copy(_z_value_t *dst, const _z_value_t *src); z_result_t _z_value_move(_z_value_t *dst, _z_value_t *src); void _z_value_clear(_z_value_t *src); void _z_value_free(_z_value_t **hello); /** * A hello message returned by a zenoh entity to a scout message sent with :c:func:`_z_scout`. * * Members: * _z_slice_t zid: The Zenoh ID of the scouted entity (empty if absent). * _z_string_vec_t locators: The locators of the scouted entity. * z_whatami_t whatami: The kind of zenoh entity. */ typedef struct { _z_id_t _zid; _z_string_svec_t _locators; z_whatami_t _whatami; uint8_t _version; } _z_hello_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_hello_t _z_hello_null(void) { return (_z_hello_t){0}; } void _z_hello_clear(_z_hello_t *src); void _z_hello_free(_z_hello_t **hello); z_result_t _z_hello_copy(_z_hello_t *dst, const _z_hello_t *src); z_result_t _z_hello_move(_z_hello_t *dst, _z_hello_t *src); bool _z_hello_check(const _z_hello_t *hello); _Z_ELEM_DEFINE(_z_hello, _z_hello_t, _z_noop_size, _z_hello_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_hello, _z_hello_t, false) typedef struct { _z_zint_t n; } _z_target_complete_body_t; typedef struct { _z_entity_global_id_t _source_id; uint32_t _source_sn; } _z_source_info_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_source_info_t _z_source_info_null(void) { return (_z_source_info_t){0}; } static inline bool _z_source_info_check(const _z_source_info_t *info) { return _z_entity_global_id_check(&info->_source_id); } typedef struct { uint32_t _request_id; uint32_t _entity_id; } _z_reply_context_t; #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_CORE_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/core.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_CORE_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_CORE_H #include "zenoh-pico/protocol/core.h" #define _Z_DEFAULT_UNICAST_BATCH_SIZE 65535 #define _Z_DEFAULT_MULTICAST_BATCH_SIZE 8192 #define _Z_DEFAULT_RESOLUTION_SIZE 2 #define _Z_DECLARE_CLEAR(layer, name) void _z_##layer##_msg_clear_##name(_z_##name##_t *m, uint8_t header) #define _Z_DECLARE_CLEAR_NOH(layer, name) void _z_##layer##_msg_clear_##name(_z_##name##_t *m) // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. #define _Z_MSG_LEN_ENC_SIZE 2 /*=============================*/ /* Message header */ /*=============================*/ #define _Z_MID_MASK 0x1f #define _Z_FLAGS_MASK 0xe0 /*=============================*/ /* Message helpers */ /*=============================*/ #define _Z_MID(h) (_Z_MID_MASK & (h)) #define _Z_FLAGS(h) (_Z_FLAGS_MASK & (h)) #define _Z_HAS_FLAG(h, f) (((h) & (f)) != 0) #define _Z_SET_FLAG(h, f) (h |= f) #define _Z_CLEAR_FLAG(h, f) (h &= (uint8_t)(~(f))) #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_CORE_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/declarations.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_DECLARATIONS_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_DECLARATIONS_H #include #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif typedef struct { uint16_t _id; _z_wireexpr_t _keyexpr; } _z_decl_kexpr_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_decl_kexpr_t _z_decl_kexpr_null(void) { return (_z_decl_kexpr_t){0}; } typedef struct { uint16_t _id; } _z_undecl_kexpr_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_undecl_kexpr_t _z_undecl_kexpr_null(void) { return (_z_undecl_kexpr_t){0}; } typedef struct { _z_wireexpr_t _keyexpr; uint32_t _id; } _z_decl_subscriber_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_decl_subscriber_t _z_decl_subscriber_null(void) { return (_z_decl_subscriber_t){0}; } typedef struct { uint32_t _id; _z_wireexpr_t _ext_keyexpr; } _z_undecl_subscriber_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_undecl_subscriber_t _z_undecl_subscriber_null(void) { return (_z_undecl_subscriber_t){0}; } typedef struct { _z_wireexpr_t _keyexpr; uint32_t _id; struct { bool _complete; uint16_t _distance; } _ext_queryable_info; } _z_decl_queryable_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_decl_queryable_t _z_decl_queryable_null(void) { return (_z_decl_queryable_t){0}; } typedef struct { uint32_t _id; _z_wireexpr_t _ext_keyexpr; } _z_undecl_queryable_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_undecl_queryable_t _z_undecl_queryable_null(void) { return (_z_undecl_queryable_t){0}; } typedef struct { _z_wireexpr_t _keyexpr; uint32_t _id; } _z_decl_token_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_decl_token_t _z_decl_token_null(void) { return (_z_decl_token_t){0}; } typedef struct { uint32_t _id; _z_wireexpr_t _ext_keyexpr; } _z_undecl_token_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_undecl_token_t _z_undecl_token_null(void) { return (_z_undecl_token_t){0}; } typedef struct { bool _placeholder; // In case we add extensions } _z_decl_final_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_decl_final_t _z_decl_final_null(void) { return (_z_decl_final_t){0}; } typedef struct { enum { _Z_DECL_KEXPR, _Z_UNDECL_KEXPR, _Z_DECL_SUBSCRIBER, _Z_UNDECL_SUBSCRIBER, _Z_DECL_QUERYABLE, _Z_UNDECL_QUERYABLE, _Z_DECL_TOKEN, _Z_UNDECL_TOKEN, _Z_DECL_FINAL, } _tag; union { _z_decl_kexpr_t _decl_kexpr; _z_undecl_kexpr_t _undecl_kexpr; _z_decl_subscriber_t _decl_subscriber; _z_undecl_subscriber_t _undecl_subscriber; _z_decl_queryable_t _decl_queryable; _z_undecl_queryable_t _undecl_queryable; _z_decl_token_t _decl_token; _z_undecl_token_t _undecl_token; _z_decl_final_t _decl_final; } _body; } _z_declaration_t; void _z_declaration_clear(_z_declaration_t* decl); _z_declaration_t _z_make_decl_keyexpr(uint16_t id, _Z_MOVE(_z_wireexpr_t) key); _z_declaration_t _z_make_undecl_keyexpr(uint16_t id); _z_declaration_t _z_make_decl_subscriber(_Z_MOVE(_z_wireexpr_t) key, uint32_t id); _z_declaration_t _z_make_undecl_subscriber(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key); _z_declaration_t _z_make_decl_queryable(_Z_MOVE(_z_wireexpr_t) key, uint32_t id, bool complete, uint16_t distance); _z_declaration_t _z_make_undecl_queryable(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key); _z_declaration_t _z_make_decl_token(_Z_MOVE(_z_wireexpr_t) key, uint32_t id); _z_declaration_t _z_make_undecl_token(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key); _z_declaration_t _z_make_decl_final(void); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_DECLARATIONS_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/interest.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_INTEREST_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_INTEREST_H #include #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif #define _Z_INTEREST_FLAG_KEYEXPRS (1) #define _Z_INTEREST_FLAG_SUBSCRIBERS (1 << 1) #define _Z_INTEREST_FLAG_QUERYABLES (1 << 2) #define _Z_INTEREST_FLAG_TOKENS (1 << 3) #define _Z_INTEREST_FLAG_RESTRICTED (1 << 4) #define _Z_INTEREST_FLAG_CURRENT (1 << 5) #define _Z_INTEREST_FLAG_FUTURE (1 << 6) #define _Z_INTEREST_FLAG_AGGREGATE (1 << 7) #define _Z_INTEREST_NOT_FINAL_MASK (_Z_INTEREST_FLAG_CURRENT | _Z_INTEREST_FLAG_FUTURE) typedef struct { _z_wireexpr_t _keyexpr; uint32_t _id; uint8_t flags; bool complete; } _z_interest_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_interest_t _z_interest_null(void) { return (_z_interest_t){0}; } void _z_interest_clear(_z_interest_t* decl); _z_interest_t _z_make_interest(_Z_MOVE(_z_wireexpr_t) key, uint32_t id, uint8_t flags); _z_interest_t _z_make_interest_final(uint32_t id); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_INTEREST_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/message.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_MESSAGE_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_MESSAGE_H #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/core.h" #ifdef __cplusplus extern "C" { #endif /* Zenoh Messages */ #define _Z_MID_Z_OAM 0x00 #define _Z_MID_Z_PUT 0x01 #define _Z_MID_Z_DEL 0x02 #define _Z_MID_Z_QUERY 0x03 #define _Z_MID_Z_REPLY 0x04 #define _Z_MID_Z_ERR 0x05 /* Zenoh message flags */ #define _Z_FLAG_Z_Z 0x80 #define _Z_FLAG_Z_D 0x20 // 1 << 5 | Dropping if D==1 then the message can be dropped #define _Z_FLAG_Z_K 0x80 // 1 << 7 | ResourceKey if K==1 then keyexpr is string #define _Z_FLAG_Z_R \ 0x20 // 1 << 5 | Reliable if R==1 then it concerns the reliable channel, best-effort otherwise #define _Z_FLAG_Z_X 0x00 // Unused flags are set to zero #define _Z_FRAG_BUFF_BASE_SIZE 128 // Arbitrary base size of the buffer to encode a fragment message header // Flags: // - X: Reserved // - E: Encoding If E==1 then the encoding is present // - Z: Extension If Z==1 then at least one extension is present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|E|X| ERR | // +-+-+-+---------+ // % encoding % // +---------------+ // ~ [err_exts] ~ if Z==1 // +---------------+ /// ~ pl: ~ Payload /// +---------------+ #define _Z_FLAG_Z_E_E 0x40 typedef struct { _z_encoding_t _encoding; _z_source_info_t _ext_source_info; _z_bytes_t _payload; } _z_msg_err_t; void _z_msg_err_clear(_z_msg_err_t *err); typedef struct { _z_timestamp_t _timestamp; _z_source_info_t _source_info; } _z_m_push_commons_t; typedef struct { _z_m_push_commons_t _commons; _z_bytes_t _attachment; } _z_msg_del_t; static inline void _z_msg_del_clear(_z_msg_del_t *del) { (void)del; } #define _Z_M_DEL_ID 0x02 #define _Z_FLAG_Z_D_T 0x20 typedef struct { _z_m_push_commons_t _commons; _z_bytes_t _payload; _z_encoding_t _encoding; _z_bytes_t _attachment; } _z_msg_put_t; void _z_msg_put_clear(_z_msg_put_t *); #define _Z_M_PUT_ID 0x01 #define _Z_FLAG_Z_P_E 0x40 #define _Z_FLAG_Z_P_T 0x20 /*------------------ Query Message ------------------*/ // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|P|C| QUERY | // +-+-+-+---------+ // ~ consolidation ~ if C==1 -- u8 // +---------------+ // ~ params ~ if P==1 -- // +---------------+ // ~ [qry_exts] ~ if Z==1 // +---------------+ #define _Z_FLAG_Z_Q_C 0x20 // 1 << 5 | Consolidation if C==1 then consolidation is present #define _Z_FLAG_Z_Q_P 0x40 // 1 << 6 | Params if P==1 then parameters are present typedef struct { _z_slice_t _parameters; _z_source_info_t _ext_info; _z_value_t _ext_value; z_consolidation_mode_t _consolidation; _z_bytes_t _ext_attachment; bool _implicit_anyke; // implicit _anyke parameter (i.e it is not listed in _parameters, but is still present) } _z_msg_query_t; typedef struct { bool info; bool body; bool attachment; } _z_msg_query_reqexts_t; _z_msg_query_reqexts_t _z_msg_query_required_extensions(const _z_msg_query_t *msg); void _z_msg_query_clear(_z_msg_query_t *msg); typedef struct { bool _is_put; union { _z_msg_del_t _del; _z_msg_put_t _put; } _body; } _z_reply_body_t; // Flags: // - C: Consolidation If C==1 then consolidation is present // - X: Reserved // - Z: Extension If Z==1 then at least one extension is present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|C| REPLY | // +-+-+-+---------+ // ~ consolidation ~ if C==1 // +---------------+ // ~ [repl_exts] ~ if Z==1 // +---------------+ // ~ ReplyBody ~ -- Payload // +---------------+ typedef struct { z_consolidation_mode_t _consolidation; _z_reply_body_t _body; } _z_msg_reply_t; void _z_msg_reply_clear(_z_msg_reply_t *msg); #define _Z_FLAG_Z_R_C 0x20 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_MESSAGE_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/network.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_NETWORK_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_NETWORK_H #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/ext.h" #ifdef __cplusplus extern "C" { #endif /* Network Messages */ #define _Z_MID_N_OAM 0x1f #define _Z_MID_N_DECLARE 0x1e #define _Z_MID_N_PUSH 0x1d #define _Z_MID_N_REQUEST 0x1c #define _Z_MID_N_RESPONSE 0x1b #define _Z_MID_N_RESPONSE_FINAL 0x1a #define _Z_MID_N_INTEREST 0x19 /*=============================*/ /* Network flags */ /*=============================*/ #define _Z_FLAG_N_Z 0x80 // 1 << 7 // DECLARE message flags: // - I: Interest If I==1 then the declare is in a response to an Interest with future==false // - X: Reserved // - Z: Extension If Z==1 then Zenoh extensions are present #define _Z_FLAG_N_DECLARE_I 0x20 // 1 << 5 // INTEREST message flags: // - C: Current If C==1 then interest concerns current declarations // - F: Future If F==1 then interest concerns future declarations // - Z: Extension If Z==1 then Zenoh extensions are present #define _Z_FLAG_N_INTEREST_CURRENT 0x20 // 1 << 5 #define _Z_FLAG_N_INTEREST_FUTURE 0x40 // 1 << 6 // PUSH message flags: // N Named if N==1 then the key expr has name/suffix // M Mapping if M==1 then keyexpr mapping is the one declared by the sender, otherwise by the receiver // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_N_PUSH_N 0x20 // 1 << 5 #define _Z_FLAG_N_PUSH_M 0x40 // 1 << 6 // REQUEST message flags: // N Named if N==1 then the key expr has name/suffix // M Mapping if M==1 then keyexpr mapping is the one declared by the sender, otherwise by the receiver // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_N_REQUEST_N 0x20 // 1 << 5 #define _Z_FLAG_N_REQUEST_M 0x40 // 1 << 6 // RESPONSE message flags: // N Named if N==1 then the key expr has name/suffix // M Mapping if M==1 then keyexpr mapping is the one declared by the sender, otherwise by the receiver // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_N_RESPONSE_N 0x20 // 1 << 5 #define _Z_FLAG_N_RESPONSE_M 0x40 // 1 << 6 typedef _z_qos_t _z_n_qos_t; #define _Z_N_QOS_IS_EXPRESS_FLAG (1 << 4) static inline _z_qos_t _z_n_qos_create(bool express, z_congestion_control_t congestion_control, z_priority_t priority) { _z_n_qos_t ret; bool nodrop = congestion_control == Z_CONGESTION_CONTROL_DROP ? 0 : 1; ret._val = (uint8_t)((express << 4) | (nodrop << 3) | (uint8_t)priority); return ret; } static inline z_priority_t _z_n_qos_get_priority(_z_n_qos_t n_qos) { z_priority_t ret = (z_priority_t)(n_qos._val & 0x07); // 0b0111 return ret; } static inline z_congestion_control_t _z_n_qos_get_congestion_control(_z_n_qos_t n_qos) { z_congestion_control_t ret = (n_qos._val & 0x08) ? Z_CONGESTION_CONTROL_BLOCK : Z_CONGESTION_CONTROL_DROP; // 0b1000 return ret; } static inline bool _z_n_qos_get_express(_z_n_qos_t n_qos) { bool ret = (n_qos._val & 0x10) != 0; // 0b10000 return ret; } #define _z_n_qos_make(express, nodrop, priority) \ _z_n_qos_create((bool)express, nodrop ? Z_CONGESTION_CONTROL_BLOCK : Z_CONGESTION_CONTROL_DROP, \ (z_priority_t)priority) extern const _z_qos_t _Z_N_QOS_DEFAULT; // RESPONSE FINAL message flags: // Z Extensions if Z==1 then Zenoh extensions are present // #define _Z_FLAG_N_RESPONSE_X 0x20 // 1 << 5 // #define _Z_FLAG_N_RESPONSE_X 0x40 // 1 << 6 // Flags: // - N: Named if N==1 then the keyexpr has name/suffix // - M: Mapping if M==1 then keyexpr mapping is the one declared by the sender, otherwise by the receiver // - Z: Extension if Z==1 then at least one extension is present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|M|N| REQUEST | // +-+-+-+---------+ // ~ request_id:z32~ // +---------------+ // ~ key_scope:z16 ~ // +---------------+ // ~ key_suffix ~ if N==1 -- // +---------------+ // ~ [req_exts] ~ if Z==1 // +---------------+ // ~ ZenohMessage ~ // +---------------+ // typedef struct { _z_zint_t _rid; _z_wireexpr_t _key; _z_timestamp_t _ext_timestamp; _z_n_qos_t _ext_qos; z_query_target_t _ext_target; uint32_t _ext_budget; uint64_t _ext_timeout_ms; enum { _Z_REQUEST_QUERY, _Z_REQUEST_PUT, _Z_REQUEST_DEL, } _tag; union { _z_msg_query_t _query; _z_msg_put_t _put; _z_msg_del_t _del; } _body; } _z_n_msg_request_t; typedef struct { bool ext_qos; bool ext_tstamp; bool ext_target; bool ext_budget; bool ext_timeout_ms; uint8_t n; } _z_n_msg_request_exts_t; _z_n_msg_request_exts_t _z_n_msg_request_needed_exts(const _z_n_msg_request_t *msg); void _z_n_msg_request_clear(_z_n_msg_request_t *msg); typedef _z_reply_body_t _z_push_body_t; // Warning: None of the sub-types require a non-0 initialization. Add a init function if it changes. static inline _z_push_body_t _z_push_body_null(void) { return (_z_push_body_t){0}; } _z_push_body_t _z_push_body_steal(_z_push_body_t *msg); void _z_push_body_clear(_z_push_body_t *msg); /*------------------ Response Final Message ------------------*/ // Flags: // - Z: Extension if Z==1 then at least one extension is present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|M|N| ResFinal| // +-+-+-+---------+ // ~ request_id:z32~ // +---------------+ // ~ [reply_exts] ~ if Z==1 // +---------------+ // typedef struct { _z_zint_t _request_id; } _z_n_msg_response_final_t; void _z_n_msg_response_final_clear(_z_n_msg_response_final_t *msg); // Flags: // - N: Named if N==1 then the keyexpr has name/suffix // - M: Mapping if M==1 then keyexpr mapping is the one declared by the sender, otherwise by the receiver // - Z: Extension if Z==1 then at least one extension is present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|M|N| PUSH | // +-+-+-+---------+ // ~ key_scope:z? ~ // +---------------+ // ~ key_suffix ~ if N==1 -- // +---------------+ // ~ [push_exts] ~ if Z==1 // +---------------+ // ~ ZenohMessage ~ // +---------------+ // typedef struct { _z_wireexpr_t _key; _z_timestamp_t _timestamp; _z_n_qos_t _qos; _z_push_body_t _body; } _z_n_msg_push_t; void _z_n_msg_push_clear(_z_n_msg_push_t *msg); /*------------------ Response Message ------------------*/ typedef struct { _z_timestamp_t _ext_timestamp; _z_zint_t _request_id; _z_wireexpr_t _key; _z_n_qos_t _ext_qos; struct { _z_id_t _zid; uint32_t _eid; } _ext_responder; enum { _Z_RESPONSE_BODY_REPLY, _Z_RESPONSE_BODY_ERR, } _tag; union { _z_msg_reply_t _reply; _z_msg_err_t _err; } _body; } _z_n_msg_response_t; void _z_n_msg_response_clear(_z_n_msg_response_t *msg); /*------------------ Declare Message ------------------*/ typedef struct { uint32_t value; bool has_value; } _z_optional_id_t; static inline _z_optional_id_t _z_optional_id_make_some(uint32_t value) { _z_optional_id_t id; id.value = value; id.has_value = true; return id; } static inline _z_optional_id_t _z_optional_id_make_none(void) { _z_optional_id_t id = {0}; return id; } typedef struct { _z_declaration_t _decl; _z_timestamp_t _ext_timestamp; _z_n_qos_t _ext_qos; _z_optional_id_t _interest_id; } _z_n_msg_declare_t; static inline void _z_n_msg_declare_clear(_z_n_msg_declare_t *msg) { _z_declaration_clear(&msg->_decl); } /*------------------ Interest Message ------------------*/ /// Flags: /// - C: Current If C==1 then interest concerns current declarations /// - F: Future If F==1 then interest concerns future declarations /// - Z: Extension If Z==1 then Zenoh extensions are present /// If C==0 and F==0, then interest is final /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ /// |Z|F|C|INTEREST | /// +-+-+-+---------+ /// ~ id:z32 ~ /// +---------------+ /// |A|M|N|R|T|Q|S|K| (*) if interest is not final /// +---------------+ /// ~ key_scope:z16 ~ if interest is not final && R==1 /// +---------------+ /// ~ key_suffix ~ if interest is not final && R==1 && N==1 -- /// +---------------+ /// ~ [int_exts] ~ if Z==1 /// +---------------+ /// /// (*) - if K==1 then the interest refers to key expressions /// - if S==1 then the interest refers to subscribers /// - if Q==1 then the interest refers to queryables /// - if T==1 then the interest refers to tokens /// - if R==1 then the interest is restricted to the matching key expression, else it is for all key expressions. /// - if N==1 then the key expr has name/suffix. If R==0 then N should be set to 0. /// - if M==1 then key expr mapping is the one declared by the sender, else it is the one declared by the receiver. /// If R==0 then M should be set to 0. /// - if A==1 then the replies SHOULD be aggregated /// ``` typedef struct { _z_interest_t _interest; } _z_n_msg_interest_t; static inline void _z_n_msg_interest_clear(_z_n_msg_interest_t *msg) { _z_interest_clear(&msg->_interest); } /*------------------ OAM Message ------------------*/ /// Flags: /// - E |: Encoding The encoding of the extension /// - E/ /// - Z: Extension If Z==1 then at least one extension is present /// /// 7 6 5 4 3 2 1 0 /// +-+-+-+-+-+-+-+-+ /// |Z|ENC| OAM | /// +-+-+-+---------+ /// ~ id:z16 ~ /// +---------------+ /// ~ [oam_exts] ~ if Z==1 /// +---------------+ /// % length % If ENC == Z64 || ENC == ZBuf (z32) /// +---------------+ /// ~ [u8] ~ If ENC == ZBuf /// +---------------+ /// /// Encoding: /// - 0b00: Unit /// - 0b01: Z64 /// - 0b10: ZBuf /// - 0b11: Reserved typedef struct { uint16_t _id; _z_timestamp_t _ext_timestamp; _z_n_qos_t _ext_qos; enum { _Z_OAM_BODY_UNIT, _Z_OAM_BODY_ZINT, _Z_OAM_BODY_ZBUF } _enc; _z_msg_ext_body_t _body; } _z_n_msg_oam_t; void _z_n_msg_oam_clear(_z_n_msg_oam_t *msg); /*------------------ Zenoh Message ------------------*/ typedef union { _z_n_msg_declare_t _declare; _z_n_msg_push_t _push; _z_n_msg_request_t _request; _z_n_msg_response_t _response; _z_n_msg_response_final_t _response_final; _z_n_msg_interest_t _interest; _z_n_msg_oam_t _oam; } _z_network_body_t; typedef struct { enum { _Z_N_DECLARE, _Z_N_PUSH, _Z_N_REQUEST, _Z_N_RESPONSE, _Z_N_RESPONSE_FINAL, _Z_N_INTEREST, _Z_N_OAM } _tag; _z_network_body_t _body; z_reliability_t _reliability; } _z_network_message_t; typedef _z_network_message_t _z_zenoh_message_t; void _z_n_msg_clear(_z_network_message_t *m); void _z_n_msg_free(_z_network_message_t **m); inline static void _z_msg_clear(_z_zenoh_message_t *msg) { _z_n_msg_clear(msg); } inline static void _z_msg_free(_z_zenoh_message_t **msg) { _z_n_msg_free(msg); } z_result_t _z_n_msg_copy(_z_network_message_t *dst, const _z_network_message_t *src); _z_network_message_t *_z_n_msg_clone(const _z_network_message_t *src); _Z_ELEM_DEFINE(_z_network_message, _z_network_message_t, _z_noop_size, _z_n_msg_clear, _z_n_msg_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SVEC_DEFINE(_z_network_message, _z_network_message_t) _Z_SLIST_DEFINE(_z_network_message, _z_network_message_t, true) void _z_n_msg_make_response_final(_z_network_message_t *msg, _z_zint_t rid); void _z_n_msg_make_declare(_z_network_message_t *msg, _z_declaration_t declaration, _z_optional_id_t interest_id); void _z_n_msg_make_query(_z_zenoh_message_t *msg, const _z_wireexpr_t *key, const _z_slice_t *parameters, _z_zint_t qid, z_reliability_t reliability, z_consolidation_mode_t consolidation, const _z_bytes_t *payload, const _z_encoding_t *encoding, uint64_t timeout_ms, const _z_bytes_t *attachment, _z_n_qos_t qos, const _z_source_info_t *source_info, bool implicit_anyke); void _z_n_msg_make_push_put(_z_network_message_t *dst, const _z_wireexpr_t *key, const _z_bytes_t *payload, const _z_encoding_t *encoding, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info); void _z_n_msg_make_push_del(_z_network_message_t *dst, const _z_wireexpr_t *key, _z_n_qos_t qos, const _z_timestamp_t *timestamp, z_reliability_t reliability, const _z_source_info_t *source_info); void _z_n_msg_make_reply_ok_put(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, const _z_wireexpr_t *key, z_reliability_t reliability, z_consolidation_mode_t consolidation, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_source_info_t *source_info, const _z_bytes_t *payload, const _z_encoding_t *encoding, const _z_bytes_t *attachment); void _z_n_msg_make_reply_ok_del(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, const _z_wireexpr_t *key, z_reliability_t reliability, z_consolidation_mode_t consolidation, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_source_info_t *source_info, const _z_bytes_t *attachment); void _z_n_msg_make_reply_err(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, z_reliability_t reliability, _z_n_qos_t qos, const _z_bytes_t *payload, const _z_encoding_t *encoding, const _z_source_info_t *source_info); void _z_n_msg_make_interest(_z_network_message_t *msg, _z_interest_t interest); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_NETWORK_H */ ================================================ FILE: include/zenoh-pico/protocol/definitions/serial.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_SERIAL_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_SERIAL_H #include #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/protocol/definitions/network.h" #ifdef __cplusplus extern "C" { #endif /// ZSerial Frame Format /// /// Using COBS /// /// +-+-+----+------------+--------+-+ /// |O|H|XXXX|ZZZZ....ZZZZ|CCCCCCCC|0| /// +-+----+------------+--------+-+ /// |O| |Len | Data | CRC32 |C| /// +-+-+-2--+----N-------+---4----+-+ /// /// Header: 1byte /// +---------------+ /// |7|6|5|4|3|2|1|0| /// +---------------+ /// |x|x|x|x|x|R|A|I| /// +---------------+ /// /// Flags: /// I - Init /// A - Ack /// R - Reset /// /// Max Frame Size: 1510 /// Max MTU: 1500 /// Max On-the-wire length: 1516 (MFS + Overhead Byte (OHB) + Kind Byte + End of packet (EOP)) #define _Z_FLAG_SERIAL_INIT 0x01 #define _Z_FLAG_SERIAL_ACK 0x02 #define _Z_FLAG_SERIAL_RESET 0x04 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_SERIAL_H*/ ================================================ FILE: include/zenoh-pico/protocol/definitions/transport.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_TRANSPORT_H #define INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_TRANSPORT_H /* Scouting Messages */ #include #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/protocol/definitions/network.h" #ifdef __cplusplus extern "C" { #endif #define _Z_MID_SCOUT 0x01 #define _Z_MID_HELLO 0x02 /* Transport Messages */ #define _Z_MID_T_OAM 0x00 #define _Z_MID_T_INIT 0x01 #define _Z_MID_T_OPEN 0x02 #define _Z_MID_T_CLOSE 0x03 #define _Z_MID_T_KEEP_ALIVE 0x04 #define _Z_MID_T_FRAME 0x05 #define _Z_MID_T_FRAGMENT 0x06 #define _Z_MID_T_JOIN 0x07 /*=============================*/ /* Message flags */ /*=============================*/ #define _Z_FLAG_T_Z 0x80 // 1 << 7 // Scout message flags: // I ZenohID if I==1 then the ZenohID is present // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_SCOUT_I 0x08 // 1 << 3 // Hello message flags: // L Locators if L==1 then Locators are present // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_HELLO_L 0x20 // 1 << 5 // Join message flags: // T Lease period if T==1 then the lease period is in seconds else in milliseconds // S Size params if S==1 then size parameters are exchanged // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_JOIN_T 0x20 // 1 << 5 #define _Z_FLAG_T_JOIN_S 0x40 // 1 << 6 // Init message flags: // A Ack if A==1 then the message is an acknowledgment (aka InitAck), otherwise InitSyn // S Size params if S==1 then size parameters are exchanged // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_INIT_A 0x20 // 1 << 5 #define _Z_FLAG_T_INIT_S 0x40 // 1 << 6 // Open message flags: // A Ack if A==1 then the message is an acknowledgment (aka OpenAck), otherwise OpenSyn // T Lease period if T==1 then the lease period is in seconds else in milliseconds // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_OPEN_A 0x20 // 1 << 5 #define _Z_FLAG_T_OPEN_T 0x40 // 1 << 6 // Frame message flags: // R Reliable if R==1 it concerns the reliable channel, else the best-effort channel // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_FRAME_R 0x20 // 1 << 5 // Frame message flags: // R Reliable if R==1 it concerns the reliable channel, else the best-effort channel // M More if M==1 then other fragments will follow // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_FRAGMENT_R 0x20 // 1 << 5 #define _Z_FLAG_T_FRAGMENT_M 0x40 // 1 << 6 // Close message flags: // S Session Close if S==1 Session close or S==0 Link close // Z Extensions if Z==1 then Zenoh extensions are present #define _Z_FLAG_T_CLOSE_S 0x20 // 1 << 5 /*=============================*/ /* Patch */ /*=============================*/ /// Used to negotiate the patch version of the protocol /// if not present (or 0), then protocol as released with 1.0.0 /// if >= 1, then fragmentation start/stop marker #define _Z_NO_PATCH 0x00 #define _Z_CURRENT_PATCH 0x01 #define _Z_PATCH_HAS_FRAGMENT_MARKERS(patch) (patch >= 1) /*=============================*/ /* Transport Messages */ /*=============================*/ /*------------------ Scout Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The SCOUT message can be sent at any point in time to solicit HELLO messages from matching parties. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|X| SCOUT | // +-+-+-+---------+ // | version | // +---------------+ // |zid_len|I| what| (#)(*) // +-+-+-+-+-+-+-+-+ // ~ [u8] ~ if Flag(I)==1 -- ZenohID // +---------------+ // // (#) ZID length. If Flag(I)==1 it indicates how many bytes are used for the ZenohID bytes. // A ZenohID is minimum 1 byte and maximum 16 bytes. Therefore, the actual length is computed as: // real_zid_len := 1 + zid_len // // (*) What. It indicates a bitmap of WhatAmI interests. // The valid bitflags are: // - 0b001: Router // - 0b010: Peer // - 0b100: Client // typedef struct { _z_id_t _zid; z_what_t _what; uint8_t _version; } _z_s_msg_scout_t; void _z_s_msg_scout_clear(_z_s_msg_scout_t *msg); /*------------------ Hello Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The HELLO message is sent in any of the following three cases: // 1) in response to a SCOUT message; // 2) to (periodically) advertise (e.g., on multicast) the Peer and the locators it is reachable at; // 3) in a already established session to update the corresponding peer on the new capabilities // (i.e., whatami) and/or new set of locators (i.e., added or deleted). // Locators are expressed as: // // udp/192.168.0.2:1234 // tcp/192.168.0.2:1234 // udp/239.255.255.123:5555 // // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|L| HELLO | // +-+-+-+---------+ // | version | // +---------------+ // |zid_len|X|X|wai| (*) // +-+-+-+-+-+-+-+-+ // ~ [u8] ~ -- ZenohID // +---------------+ // ~ ~ if Flag(L)==1 -- List of locators // +---------------+ // // (*) WhatAmI. It indicates the role of the zenoh node sending the HELLO message. // The valid WhatAmI values are: // - 0b00: Router // - 0b01: Peer // - 0b10: Client // - 0b11: Reserved // typedef struct { _z_id_t _zid; _z_locator_array_t _locators; z_whatami_t _whatami; uint8_t _version; } _z_s_msg_hello_t; void _z_s_msg_hello_clear(_z_s_msg_hello_t *msg); /*------------------ Join Message ------------------*/ // # Join message // // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The JOIN message is sent on a multicast Locator to advertise the transport parameters. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |O|S|T| JOIN | // +-+-+-+-+-------+ // ~ |Q~ if O==1 // +---------------+ // | v_maj | v_min | -- Protocol Version VMaj.VMin // +-------+-------+ // ~ whatami ~ -- Router, Peer or a combination of them // +---------------+ // ~ zenoh_id ~ -- PID of the sender of the JOIN message // +---------------+ // ~ lease ~ -- Lease period of the sender of the JOIN message(*) // +---------------+ // ~ seq_num_res ~ if S==1(*) -- Otherwise 2^28 is assumed(**) // +---------------+ // ~ [next_sn] ~ (***) // +---------------+ // // - if Q==1 then the sender supports QoS. // // (*) if T==1 then the lease period is expressed in seconds, otherwise in milliseconds // (**) if S==0 then 2^28 is assumed. // (***) if Q==1 then 8 sequence numbers are present: one for each priority. // if Q==0 then only one sequence number is present. // typedef struct { _z_zint_t _reliable; _z_zint_t _best_effort; } _z_coundit_sn_t; typedef struct { union { _z_coundit_sn_t _plain; _z_coundit_sn_t _qos[Z_PRIORITIES_NUM]; } _val; bool _is_qos; } _z_conduit_sn_list_t; typedef struct { _z_id_t _zid; _z_zint_t _lease; _z_conduit_sn_list_t _next_sn; uint16_t _batch_size; z_whatami_t _whatami; uint8_t _req_id_res; uint8_t _seq_num_res; uint8_t _version; #if Z_FEATURE_FRAGMENTATION == 1 uint8_t _patch; #endif } _z_t_msg_join_t; void _z_t_msg_join_clear(_z_t_msg_join_t *msg); /*------------------ Init Message ------------------*/ // # Init message // // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The INIT message is sent on a specific Locator to initiate a session with the peer associated // with that Locator. The initiator MUST send an INIT message with the A flag set to 0. If the // corresponding peer deems appropriate to initialize a session with the initiator, the corresponding // peer MUST reply with an INIT message with the A flag set to 1. // // Flags: // - A: Ack if A==0 then the message is an InitSyn else it is an InitAck // - S: Size params if S==1 then size parameters are exchanged // - Z: Extensions if Z==1 then zenoh extensions will follow. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|S|A| INIT | // +-+-+-+---------+ // | version | // +---------------+ // |zid_len|x|x|wai| (#)(*) // +-------+-+-+---+ // ~ [u8] ~ -- ZenohID of the sender of the INIT message // +---------------+ // |x|x|kid|rid|fsn| \ -- SN/ID resolution (+) // +---------------+ | if Flag(S)==1 // | u16 | | -- Batch Size ($) // | | / // +---------------+ // ~ ~ -- if Flag(A)==1 -- Cookie // +---------------+ // ~ [InitExts] ~ -- if Flag(Z)==1 // +---------------+ // // If A==1 and S==0 then size parameters are (ie. S flag) are accepted. // // (*) WhatAmI. It indicates the role of the zenoh node sending the INIT // message. // The valid WhatAmI values are: // - 0b00: Router // - 0b01: Peer // - 0b10: Client // - 0b11: Reserved // // (#) ZID length. It indicates how many bytes are used for the ZenohID bytes. // A ZenohID is minimum 1 byte and maximum 16 bytes. Therefore, the actual // length is computed as: // real_zid_len := 1 + zid_len // // (+) Sequence Number/ID resolution. It indicates the resolution and // consequently the wire overhead // of various SN and ID in Zenoh. // - fsn: frame/fragment sequence number resolution. Used in Frame/Fragment // messages. // - rid: request ID resolution. Used in Request/Response messages. // - kid: key expression ID resolution. Used in Push/Request/Response // messages. The valid SN/ID resolution values are: // - 0b00: 8 bits // - 0b01: 16 bits // - 0b10: 32 bits // - 0b11: 64 bits // // ($) Batch Size. It indicates the maximum size of a batch the sender of the // typedef struct { _z_id_t _zid; _z_slice_t _cookie; uint16_t _batch_size; z_whatami_t _whatami; uint8_t _req_id_res; uint8_t _seq_num_res; uint8_t _version; #if Z_FEATURE_FRAGMENTATION == 1 uint8_t _patch; #endif } _z_t_msg_init_t; void _z_t_msg_init_clear(_z_t_msg_init_t *msg); /*------------------ Open Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The OPEN message is sent on a link to finally open an initialized session with the peer. // // Flags: // - A Ack if A==1 then the message is an acknowledgment (aka OpenAck), otherwise OpenSyn // - T Lease period if T==1 then the lease period is in seconds else in milliseconds // - Z Extensions if Z==1 then Zenoh extensions are present // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|T|A| OPEN | // +-+-+-+---------+ // % lease % -- Lease period of the sender of the OPEN message // +---------------+ // % initial_sn % -- Initial SN proposed by the sender of the OPEN(*) // +---------------+ // ~ ~ if Flag(A)==0 (**) -- Cookie // +---------------+ // ~ [OpenExts] ~ if Flag(Z)==1 // +---------------+ // // (*) The initial sequence number MUST be compatible with the sequence number resolution agreed in the // [`super::InitSyn`]-[`super::InitAck`] message exchange // (**) The cookie MUST be the same received in the [`super::InitAck`]from the corresponding zenoh node // typedef struct { _z_zint_t _lease; _z_zint_t _initial_sn; _z_slice_t _cookie; } _z_t_msg_open_t; void _z_t_msg_open_clear(_z_t_msg_open_t *msg); /*------------------ Close Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The CLOSE message is sent in any of the following two cases: // 1) in response to an OPEN message which is not accepted; // 2) at any time to arbitrarily close the session with the corresponding peer. // // Flags: // - S: Session Close if S==1 Session close or S==0 Link close // - X: Reserved // - Z: Extensions if Z==1 then zenoh extensions will follow. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|S| CLOSE | // +-+-+-+---------+ // | Reason | // +---------------+ // ~ [CloseExts] ~ if Flag(Z)==1 // +---------------+ // typedef struct { uint8_t _reason; } _z_t_msg_close_t; void _z_t_msg_close_clear(_z_t_msg_close_t *msg); /*=============================*/ /* Close reasons */ /*=============================*/ #define _Z_CLOSE_GENERIC 0x00 #define _Z_CLOSE_UNSUPPORTED 0x01 #define _Z_CLOSE_INVALID 0x02 #define _Z_CLOSE_MAX_TRANSPORTS 0x03 #define _Z_CLOSE_MAX_LINKS 0x04 #define _Z_CLOSE_EXPIRED 0x05 /*------------------ Keep Alive Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // The KEEP_ALIVE message can be sent periodically to avoid the expiration of the session lease // period in case there are no messages to be sent. // // Flags: // - X: Reserved // - X: Reserved // - Z: Extensions If Z==1 then Zenoh extensions will follow. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|X| KALIVE | // +-+-+-+---------+ // ~ [KAliveExts] ~ if Flag(Z)==1 // +---------------+ // typedef struct { uint8_t __dummy; // Just to avoid empty structures that might cause undefined behavior } _z_t_msg_keep_alive_t; void _z_t_msg_keep_alive_clear(_z_t_msg_keep_alive_t *msg); /*------------------ Frame Message ------------------*/ // NOTE: 16 bits (2 bytes) may be prepended to the serialized message indicating the total length // in bytes of the message, resulting in the maximum length of a message being 65_535 bytes. // This is necessary in those stream-oriented transports (e.g., TCP) that do not preserve // the boundary of the serialized messages. The length is encoded as little-endian. // In any case, the length of a message must not exceed 65_535 bytes. // // Flags: // - R: Reliable If R==1 it concerns the reliable channel, else the best-effort channel // - X: Reserved // - Z: Extensions If Z==1 then zenoh extensions will follow. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|X|R| FRAME | // +-+-+-+---------+ // % seq num % // +---------------+ // ~ [FrameExts] ~ if Flag(Z)==1 // +---------------+ // ~ [NetworkMsg] ~ // +---------------+ // // - if R==1 then the FRAME is sent on the reliable channel, best-effort otherwise. // typedef struct { _z_zbuf_t *_payload; _z_zint_t _sn; } _z_t_msg_frame_t; void _z_t_msg_frame_clear(_z_t_msg_frame_t *msg); /*------------------ Fragment Message ------------------*/ // The Fragment message is used to transmit on the wire large Zenoh Message that require fragmentation // because they are larger than the maximum batch size (i.e. 2^16-1) and/or the link MTU. // // The [`Fragment`] message flow is the following: // // Flags: // - R: Reliable if R==1 it concerns the reliable channel, else the best-effort channel // - M: More if M==1 then other fragments will follow // - Z: Extensions if Z==1 then zenoh extensions will follow. // // 7 6 5 4 3 2 1 0 // +-+-+-+-+-+-+-+-+ // |Z|M|R| FRAGMENT| // +-+-+-+---------+ // % seq num % // +---------------+ // ~ [FragExts] ~ if Flag(Z)==1 // +---------------+ // ~ [u8] ~ // +---------------+ // typedef struct { _z_slice_t _payload; _z_zint_t _sn; bool first; bool drop; } _z_t_msg_fragment_t; void _z_t_msg_fragment_clear(_z_t_msg_fragment_t *msg); /*------------------ Transport Message ------------------*/ typedef union { _z_t_msg_join_t _join; _z_t_msg_init_t _init; _z_t_msg_open_t _open; _z_t_msg_close_t _close; _z_t_msg_keep_alive_t _keep_alive; _z_t_msg_frame_t _frame; _z_t_msg_fragment_t _fragment; } _z_transport_body_t; typedef struct { _z_transport_body_t _body; uint8_t _header; } _z_transport_message_t; void _z_t_msg_clear(_z_transport_message_t *msg); z_reliability_t _z_t_msg_get_reliability(_z_transport_message_t *msg); /*------------------ Builders ------------------*/ _z_transport_message_t _z_t_msg_make_join(z_whatami_t whatami, _z_zint_t lease, _z_id_t zid, _z_conduit_sn_list_t next_sn); _z_transport_message_t _z_t_msg_make_init_syn(z_whatami_t whatami, _z_id_t zid); _z_transport_message_t _z_t_msg_make_init_ack(z_whatami_t whatami, _z_id_t zid, _z_slice_t cookie); _z_transport_message_t _z_t_msg_make_open_syn(_z_zint_t lease, _z_zint_t initial_sn, _z_slice_t cookie); _z_transport_message_t _z_t_msg_make_open_ack(_z_zint_t lease, _z_zint_t initial_sn); _z_transport_message_t _z_t_msg_make_close(uint8_t reason, bool link_only); _z_transport_message_t _z_t_msg_make_keep_alive(void); _z_transport_message_t _z_t_msg_make_frame(_z_zint_t sn, _z_zbuf_t *payload, z_reliability_t reliability); _z_transport_message_t _z_t_msg_make_frame_header(_z_zint_t sn, z_reliability_t reliability); _z_transport_message_t _z_t_msg_make_fragment_header(_z_zint_t sn, z_reliability_t reliability, bool is_last, bool first, bool drop); _z_transport_message_t _z_t_msg_make_fragment(_z_zint_t sn, _z_slice_t messages, z_reliability_t reliability, bool is_last, bool first, bool drop); /*------------------ Copy ------------------*/ void _z_t_msg_copy(_z_transport_message_t *clone, _z_transport_message_t *msg); void _z_t_msg_copy_join(_z_t_msg_join_t *clone, _z_t_msg_join_t *msg); void _z_t_msg_copy_init(_z_t_msg_init_t *clone, _z_t_msg_init_t *msg); void _z_t_msg_copy_open(_z_t_msg_open_t *clone, _z_t_msg_open_t *msg); void _z_t_msg_copy_close(_z_t_msg_close_t *clone, _z_t_msg_close_t *msg); void _z_t_msg_copy_keep_alive(_z_t_msg_keep_alive_t *clone, _z_t_msg_keep_alive_t *msg); void _z_t_msg_copy_frame(_z_t_msg_frame_t *clone, _z_t_msg_frame_t *msg); typedef union { _z_s_msg_scout_t _scout; _z_s_msg_hello_t _hello; } _z_scouting_body_t; typedef struct { _z_scouting_body_t _body; uint8_t _header; } _z_scouting_message_t; void _z_s_msg_clear(_z_scouting_message_t *msg); _z_scouting_message_t _z_s_msg_make_scout(z_what_t what, _z_id_t zid); _z_scouting_message_t _z_s_msg_make_hello(z_whatami_t whatami, _z_id_t zid, _z_locator_array_t locators); void _z_s_msg_copy(_z_scouting_message_t *clone, _z_scouting_message_t *msg); void _z_s_msg_copy_scout(_z_s_msg_scout_t *clone, _z_s_msg_scout_t *msg); void _z_s_msg_copy_hello(_z_s_msg_hello_t *clone, _z_s_msg_hello_t *msg); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_PROTOCOL_DEFINITIONS_TRANSPORT_H */ ================================================ FILE: include/zenoh-pico/protocol/ext.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_PROTOCOL_EXTENSION_H #define ZENOH_PICO_PROTOCOL_EXTENSION_H #include #include #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif /*=============================*/ /* Message header */ /*=============================*/ #define _Z_EXT_FULL_ID_MASK 0x7F #define _Z_EXT_ID_MASK 0x0F #define _Z_EXT_ENC_MASK 0x60 /*=============================*/ /* Message helpers */ /*=============================*/ #define _Z_EXT_FULL_ID(h) (_Z_EXT_FULL_ID_MASK & h) #define _Z_EXT_ID(h) (_Z_EXT_ID_MASK & h) #define _Z_EXT_ENC(h) (_Z_EXT_ENC_MASK & h) #define _Z_EXT_HAS_FLAG(h, f) ((h & f) != 0) #define _Z_EXT_SET_FLAG(h, f) (h |= f) /*=============================*/ /* Extension IDs */ /*=============================*/ #define _Z_MSG_EXT_ID_JOIN_QOS (0x01 | _Z_MSG_EXT_FLAG_M | _Z_MSG_EXT_ENC_ZBUF) #define _Z_MSG_EXT_ID_JOIN_PATCH (0x07 | _Z_MSG_EXT_ENC_ZINT) #define _Z_MSG_EXT_ID_INIT_PATCH (0x07 | _Z_MSG_EXT_ENC_ZINT) #define _Z_MSG_EXT_ID_FRAGMENT_FIRST (0x02 | _Z_MSG_EXT_ENC_UNIT) #define _Z_MSG_EXT_ID_FRAGMENT_DROP (0x03 | _Z_MSG_EXT_ENC_UNIT) /*=============================*/ /* Extension Encodings */ /*=============================*/ #define _Z_MSG_EXT_ENC_UNIT 0x00 // 0x00 << 5 #define _Z_MSG_EXT_ENC_ZINT 0x20 // 0x01 << 5 #define _Z_MSG_EXT_ENC_ZBUF 0x40 // 0x10 << 5 /*=============================*/ /* Extension flags */ /*=============================*/ #define _Z_MSG_EXT_FLAG_M 0x10 #define _Z_MSG_EXT_IS_MANDATORY(h) ((h & _Z_MSG_EXT_FLAG_M) != 0) #define _Z_MSG_EXT_FLAG_Z 0x80 #define _Z_MSG_EXT_MORE(more) (more ? _Z_MSG_EXT_FLAG_Z : 0) typedef struct { uint8_t __dummy; // Just to avoid empty structures that might cause undefined behavior } _z_msg_ext_unit_t; void _z_msg_ext_clear_unit(_z_msg_ext_unit_t *ext); /*------------------ ZID Extension ------------------*/ typedef struct { uint64_t _val; } _z_msg_ext_zint_t; void _z_msg_ext_clear_zint(_z_msg_ext_zint_t *ext); /*------------------ Unknown Extension ------------------*/ typedef struct { _z_slice_t _val; } _z_msg_ext_zbuf_t; void _z_msg_ext_clear_zbuf(_z_msg_ext_zbuf_t *ext); /*------------------ Message Extensions ------------------*/ typedef union { _z_msg_ext_unit_t _unit; _z_msg_ext_zint_t _zint; _z_msg_ext_zbuf_t _zbuf; } _z_msg_ext_body_t; typedef struct { _z_msg_ext_body_t _body; uint8_t _header; } _z_msg_ext_t; void _z_msg_ext_clear(_z_msg_ext_t *ext); /*------------------ Builders ------------------*/ _z_msg_ext_t _z_msg_ext_make_unit(uint8_t id); _z_msg_ext_t _z_msg_ext_make_zint(uint8_t id, _z_zint_t zid); _z_msg_ext_t _z_msg_ext_make_zbuf(uint8_t id, _z_slice_t zbuf); /*------------------ Copy ------------------*/ void _z_msg_ext_copy(_z_msg_ext_t *clone, const _z_msg_ext_t *ext); void _z_msg_ext_copy_unit(_z_msg_ext_unit_t *clone, const _z_msg_ext_unit_t *ext); void _z_msg_ext_copy_zint(_z_msg_ext_zint_t *clone, const _z_msg_ext_zint_t *ext); void _z_msg_ext_copy_zbuf(_z_msg_ext_zbuf_t *clone, const _z_msg_ext_zbuf_t *ext); _Z_ELEM_DEFINE(_z_msg_ext, _z_msg_ext_t, _z_noop_size, _z_msg_ext_clear, _z_msg_ext_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_VEC_DEFINE(_z_msg_ext, _z_msg_ext_t) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_PROTOCOL_EXTENSION_H */ ================================================ FILE: include/zenoh-pico/protocol/iobuf.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_PROTOCOL_IOBUF_H #define ZENOH_PICO_PROTOCOL_IOBUF_H #include #include #include #include #include "zenoh-pico/collections/arc_slice.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/vec.h" #include "zenoh-pico/utils/pointers.h" #ifdef __cplusplus extern "C" { #endif /*------------------ IOSli ------------------*/ typedef struct { size_t _r_pos; size_t _w_pos; size_t _capacity; uint8_t *_buf; bool _is_alloc; } _z_iosli_t; static inline _z_iosli_t _z_iosli_null(void) { return (_z_iosli_t){0}; } static inline size_t _z_iosli_writable(const _z_iosli_t *ios) { return ios->_capacity - ios->_w_pos; } static inline size_t _z_iosli_readable(const _z_iosli_t *ios) { return ios->_w_pos - ios->_r_pos; } static inline bool _z_iosli_can_write(const _z_iosli_t *ios) { return ios->_capacity > ios->_w_pos; } static inline bool _z_iosli_can_read(const _z_iosli_t *ios) { return ios->_w_pos > ios->_r_pos; } static inline uint8_t _z_iosli_read(_z_iosli_t *ios) { assert(ios->_r_pos < ios->_w_pos); return ios->_buf[ios->_r_pos++]; } static inline uint8_t *_z_iosli_read_as_ref(_z_iosli_t *ios) { assert(ios->_r_pos < ios->_w_pos); return &ios->_buf[ios->_r_pos++]; } static inline void _z_iosli_write(_z_iosli_t *ios, uint8_t b) { assert(ios->_capacity > ios->_w_pos); ios->_buf[ios->_w_pos++] = b; } static inline uint8_t _z_iosli_get(const _z_iosli_t *ios, size_t pos) { assert(pos < ios->_capacity); return ios->_buf[pos]; } static inline void _z_iosli_put(_z_iosli_t *ios, uint8_t b, size_t pos) { assert(pos < ios->_capacity); ios->_buf[pos] = b; } static inline void _z_iosli_reset(_z_iosli_t *ios) { ios->_r_pos = 0; ios->_w_pos = 0; } static inline size_t _z_iosli_size(const _z_iosli_t *ios) { (void)(ios); return sizeof(_z_iosli_t); } static inline void _z_iosli_read_bytes(_z_iosli_t *ios, uint8_t *dst, size_t offset, size_t length) { assert(_z_iosli_readable(ios) >= length); uint8_t *w_pos = _z_ptr_u8_offset(dst, (ptrdiff_t)offset); (void)memcpy(w_pos, ios->_buf + ios->_r_pos, length); ios->_r_pos = ios->_r_pos + length; } static inline void _z_iosli_copy_bytes(_z_iosli_t *dst, const _z_iosli_t *src) { size_t length = _z_iosli_readable(src); assert(dst->_capacity >= length); (void)memcpy(dst->_buf + dst->_w_pos, src->_buf + src->_r_pos, length); dst->_w_pos += length; } static inline void _z_iosli_write_bytes(_z_iosli_t *ios, const uint8_t *bs, size_t offset, size_t length) { assert(_z_iosli_writable(ios) >= length); uint8_t *w_pos = _z_ptr_u8_offset(ios->_buf, (ptrdiff_t)ios->_w_pos); (void)memcpy(w_pos, _z_cptr_u8_offset(bs, (ptrdiff_t)offset), length); ios->_w_pos += length; } _z_iosli_t _z_iosli_make(size_t capacity); _z_iosli_t *_z_iosli_new(size_t capacity); _z_iosli_t _z_iosli_wrap(const uint8_t *buf, size_t length, size_t r_pos, size_t w_pos); _z_iosli_t _z_iosli_steal(_z_iosli_t *ios); _z_slice_t _z_iosli_to_bytes(const _z_iosli_t *ios); size_t _z_iosli_size(const _z_iosli_t *ios); void _z_iosli_clear(_z_iosli_t *ios); void _z_iosli_free(_z_iosli_t **ios); void _z_iosli_copy(_z_iosli_t *dst, const _z_iosli_t *src); _z_iosli_t *_z_iosli_clone(const _z_iosli_t *src); _Z_ELEM_DEFINE(_z_iosli, _z_iosli_t, _z_iosli_size, _z_iosli_clear, _z_iosli_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SVEC_DEFINE(_z_iosli, _z_iosli_t) /*------------------ ZBuf ------------------*/ typedef struct { _z_iosli_t _ios; _z_slice_simple_rc_t _slice; } _z_zbuf_t; static inline size_t _z_zbuf_get_ref_count(const _z_zbuf_t *zbf) { return _z_slice_simple_rc_count(&zbf->_slice); } static inline _z_zbuf_t _z_zbuf_null(void) { return (_z_zbuf_t){0}; } static inline void _z_zbuf_reset(_z_zbuf_t *zbf) { _z_iosli_reset(&zbf->_ios); } static inline uint8_t const *_z_zbuf_start(const _z_zbuf_t *zbf) { return _z_ptr_u8_offset(zbf->_ios._buf, (ptrdiff_t)zbf->_ios._r_pos); } static inline size_t _z_zbuf_capacity(const _z_zbuf_t *zbf) { return zbf->_ios._capacity; } static inline size_t _z_zbuf_space_left(const _z_zbuf_t *zbf) { return _z_iosli_writable(&zbf->_ios); } static inline size_t _z_zbuf_len(const _z_zbuf_t *zbf) { return _z_iosli_readable(&zbf->_ios); } static inline bool _z_zbuf_can_read(const _z_zbuf_t *zbf) { return _z_iosli_can_read(&zbf->_ios); } static inline uint8_t _z_zbuf_read(_z_zbuf_t *zbf) { return _z_iosli_read(&zbf->_ios); } static inline uint8_t *_z_zbuf_read_as_ref(_z_zbuf_t *zbf) { return _z_iosli_read_as_ref(&zbf->_ios); } static inline uint8_t _z_zbuf_get(const _z_zbuf_t *zbf, size_t pos) { return _z_iosli_get(&zbf->_ios, pos); } static inline size_t _z_zbuf_get_rpos(const _z_zbuf_t *zbf) { return zbf->_ios._r_pos; } static inline size_t _z_zbuf_get_wpos(const _z_zbuf_t *zbf) { return zbf->_ios._w_pos; } static inline void _z_zbuf_set_rpos(_z_zbuf_t *zbf, size_t r_pos) { assert(r_pos <= zbf->_ios._w_pos); zbf->_ios._r_pos = r_pos; } static inline void _z_zbuf_set_wpos(_z_zbuf_t *zbf, size_t w_pos) { assert(w_pos <= zbf->_ios._capacity); zbf->_ios._w_pos = w_pos; } static inline uint8_t *_z_zbuf_get_rptr(const _z_zbuf_t *zbf) { return zbf->_ios._buf + zbf->_ios._r_pos; } static inline uint8_t *_z_zbuf_get_wptr(const _z_zbuf_t *zbf) { return zbf->_ios._buf + zbf->_ios._w_pos; } // Constructs a _borrowing_ reader on `slice` _z_zbuf_t _z_zbuf_make(size_t capacity); _z_zbuf_t _z_zbuf_view(_z_zbuf_t *zbf, size_t length); _z_zbuf_t _z_slice_as_zbuf(_z_slice_t slice); void _z_zbuf_copy_bytes(_z_zbuf_t *dst, const _z_zbuf_t *src); void _z_zbuf_copy(_z_zbuf_t *dst, const _z_zbuf_t *src); void _z_zbuf_read_bytes(_z_zbuf_t *zbf, uint8_t *dest, size_t offset, size_t length); void _z_zbuf_compact(_z_zbuf_t *zbf); void _z_zbuf_clear(_z_zbuf_t *zbf); void _z_zbuf_free(_z_zbuf_t **zbf); /*------------------ WBuf ------------------*/ typedef struct { _z_iosli_svec_t _ioss; size_t _r_idx; size_t _w_idx; size_t _expansion_step; } _z_wbuf_t; static inline _z_wbuf_t _z_wbuf_null(void) { return (_z_wbuf_t){0}; } static inline _z_iosli_t *_z_wbuf_get_iosli(const _z_wbuf_t *wbf, size_t idx) { return _z_iosli_svec_get(&wbf->_ioss, idx); } _z_wbuf_t _z_wbuf_make(size_t capacity, bool is_expandable); size_t _z_wbuf_capacity(const _z_wbuf_t *wbf); size_t _z_wbuf_len(const _z_wbuf_t *wbf); size_t _z_wbuf_space_left(const _z_wbuf_t *wbf); z_result_t _z_wbuf_write(_z_wbuf_t *wbf, uint8_t b); z_result_t _z_wbuf_write_bytes(_z_wbuf_t *wbf, const uint8_t *bs, size_t offset, size_t length); z_result_t _z_wbuf_wrap_bytes(_z_wbuf_t *wbf, const uint8_t *bs, size_t offset, size_t length); void _z_wbuf_put(_z_wbuf_t *wbf, uint8_t b, size_t pos); size_t _z_wbuf_get_rpos(const _z_wbuf_t *wbf); size_t _z_wbuf_get_wpos(const _z_wbuf_t *wbf); void _z_wbuf_set_rpos(_z_wbuf_t *wbf, size_t r_pos); void _z_wbuf_set_wpos(_z_wbuf_t *wbf, size_t w_pos); size_t _z_wbuf_len_iosli(const _z_wbuf_t *wbf); _z_zbuf_t _z_wbuf_to_zbuf(const _z_wbuf_t *wbf); _z_zbuf_t _z_wbuf_moved_as_zbuf(_z_wbuf_t *wbf); z_result_t _z_wbuf_siphon(_z_wbuf_t *dst, _z_wbuf_t *src, size_t length); void _z_wbuf_copy(_z_wbuf_t *dst, const _z_wbuf_t *src); void _z_wbuf_reset(_z_wbuf_t *wbf); void _z_wbuf_clear(_z_wbuf_t *wbf); void _z_wbuf_free(_z_wbuf_t **wbf); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_PROTOCOL_IOBUF_H */ ================================================ FILE: include/zenoh-pico/runtime/background_executor.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COLLECTIONS_BACKGROUND_EXECUTOR_H #define ZENOH_PICO_COLLECTIONS_BACKGROUND_EXECUTOR_H #include "zenoh-pico/config.h" #if Z_FEATURE_MULTI_THREAD == 1 #include #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/runtime/executor.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif typedef struct _z_background_executor_inner_t _z_background_executor_inner_t; void _z_background_executor_inner_clear(_z_background_executor_inner_t *be); _Z_REFCOUNT_DEFINE_NO_FROM_VAL(_z_background_executor_inner, _z_background_executor_inner) typedef struct _z_background_executor_t { _z_background_executor_inner_rc_t _inner; } _z_background_executor_t; z_result_t _z_background_executor_init(_z_background_executor_t *be, z_task_attr_t *task_attr); // Initializes the executor without spawning a background thread. // Tasks can be added via _z_background_executor_spawn but won't be executed until // _z_background_executor_start is called. z_result_t _z_background_executor_init_deferred(_z_background_executor_t *be); // Spawns a background thread to run an executor that was previously created with _z_background_executor_init_deferred. // Returns _Z_RES_OK in case of success or if the executor is already running, non-zero value otherwise. z_result_t _z_background_executor_start(_z_background_executor_t *be, z_task_attr_t *task_attr); // Stops the background executor thread without destroying the executor. // Pending tasks are preserved and can be executed after restarting with _z_background_executor_start. // Returns _Z_RES_OK in case of success or if the executor was already stopped, non-zero value otherwise. z_result_t _z_background_executor_stop(_z_background_executor_t *be); static inline void _z_background_executor_null(_z_background_executor_t *be) { be->_inner = _z_background_executor_inner_rc_null(); } // Spawns a future to be executed in the background. // The caller can optionally receive a handle to the future, which can be used to check the future's status or cancel // it. If the caller does not care about the future's status, they can pass NULL as opt_handle_out. z_result_t _z_background_executor_spawn(_z_background_executor_t *be, _z_fut_t *fut, _z_fut_handle_t *opt_handle_out); z_result_t _z_background_executor_suspend(_z_background_executor_t *be); z_result_t _z_background_executor_resume(_z_background_executor_t *be); void _z_background_executor_destroy(_z_background_executor_t *be); z_result_t _z_background_executor_get_fut_status(_z_background_executor_t *be, const _z_fut_handle_t *handle, _z_fut_status_t *status_out); z_result_t _z_background_executor_cancel_fut(_z_background_executor_t *be, const _z_fut_handle_t *handle); z_result_t _z_background_executor_clone(_z_background_executor_t *dst, const _z_background_executor_t *src); bool _z_background_executor_is_running(const _z_background_executor_t *be); #ifdef __cplusplus } #endif #endif /* Z_FEATURE_MULTI_THREAD */ #endif ================================================ FILE: include/zenoh-pico/runtime/executor.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_RUNTIME_EXECUTOR_H #define ZENOH_PICO_RUNTIME_EXECUTOR_H #include #include #include "zenoh-pico/collections/atomic.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif typedef enum _z_fut_status_t { _Z_FUT_STATUS_RUNNING = 0, _Z_FUT_STATUS_READY = 1, _Z_FUT_STATUS_SLEEPING = 2, _Z_FUT_STATUS_SUSPENDED = 3, } _z_fut_status_t; typedef struct _z_fut_handle_t { size_t _id; } _z_fut_handle_t; static inline _z_fut_handle_t _z_fut_handle_null(void) { _z_fut_handle_t handle; handle._id = 0; return handle; } static inline bool _z_fut_handle_is_null(_z_fut_handle_t handle) { return handle._id == 0; } typedef struct _z_fut_fn_result_t { _z_fut_status_t _status; z_clock_t _wake_up_time; } _z_fut_fn_result_t; static inline _z_fut_fn_result_t _z_fut_fn_result_ready(void) { _z_fut_fn_result_t result; result._status = _Z_FUT_STATUS_READY; return result; } static inline _z_fut_fn_result_t _z_fut_fn_result_continue(void) { _z_fut_fn_result_t result; result._status = _Z_FUT_STATUS_RUNNING; return result; } static inline _z_fut_fn_result_t _z_fut_fn_result_suspend(void) { _z_fut_fn_result_t result; result._status = _Z_FUT_STATUS_SUSPENDED; return result; } static inline _z_fut_fn_result_t _z_fut_fn_result_wake_up_after(unsigned long wake_up_time_ms) { _z_fut_fn_result_t result; result._status = _Z_FUT_STATUS_SLEEPING; result._wake_up_time = z_clock_now(); z_clock_advance_ms(&result._wake_up_time, wake_up_time_ms); return result; } typedef struct _z_executor_t _z_executor_t; typedef _z_fut_fn_result_t (*_z_fut_fn_t)(void *arg, _z_executor_t *executor); typedef void (*_z_fut_destroy_fn_t)(void *arg); typedef struct _z_fut_t { void *_fut_arg; _z_fut_fn_t _fut_fn; _z_fut_destroy_fn_t _destroy_fn; } _z_fut_t; static inline void _z_fut_destroy(_z_fut_t *fut) { if (fut->_destroy_fn != NULL) { fut->_destroy_fn(fut->_fut_arg); } fut->_fut_arg = NULL; fut->_fut_fn = NULL; fut->_destroy_fn = NULL; } static inline void _z_fut_move(_z_fut_t *dst, _z_fut_t *src) { dst->_fut_arg = src->_fut_arg; dst->_fut_fn = src->_fut_fn; dst->_destroy_fn = src->_destroy_fn; // Clear source src->_fut_arg = NULL; src->_fut_fn = NULL; src->_destroy_fn = NULL; } static inline _z_fut_t _z_fut_new(void *arg, _z_fut_fn_t fut_fn, _z_fut_destroy_fn_t destroy_fn) { _z_fut_t fut; fut._fut_arg = arg; fut._fut_fn = fut_fn; fut._destroy_fn = destroy_fn; return fut; } static inline _z_fut_t _z_fut_null(void) { return _z_fut_new(NULL, NULL, NULL); } static inline bool _z_fut_is_null(const _z_fut_t *fut) { return fut->_fut_fn == NULL; } // _z_fut_schedule_t packs status (bits [7:0]) and wake-up time in ms (bits [63:8]) into a single uint64_t. // The wake-up time is only meaningful when status == _Z_FUT_STATUS_SLEEPING. // This gives a maximum schedulable wake-up offset of 2^56 ms (~2.28 billion years) from the executor epoch. typedef uint64_t _z_fut_schedule_t; #define _Z_FUT_SCHEDULE_STATUS_MASK ((uint64_t)0xFFu) #define _Z_FUT_SCHEDULE_TIME_SHIFT 8u static inline _z_fut_status_t _z_fut_schedule_get_status(const _z_fut_schedule_t s) { return (_z_fut_status_t)(s & _Z_FUT_SCHEDULE_STATUS_MASK); } static inline uint64_t _z_fut_schedule_get_wake_up_time_ms(const _z_fut_schedule_t s) { return s >> _Z_FUT_SCHEDULE_TIME_SHIFT; } static inline _z_fut_schedule_t _z_fut_schedule_running(void) { return (uint64_t)_Z_FUT_STATUS_RUNNING; } static inline _z_fut_schedule_t _z_fut_schedule_ready(void) { return (uint64_t)_Z_FUT_STATUS_READY; } static inline _z_fut_schedule_t _z_fut_schedule_sleeping(uint64_t wake_up_time_ms) { return (uint64_t)_Z_FUT_STATUS_SLEEPING | (wake_up_time_ms << _Z_FUT_SCHEDULE_TIME_SHIFT); } static inline _z_fut_schedule_t _z_fut_schedule_suspended(void) { return (uint64_t)_Z_FUT_STATUS_SUSPENDED; } typedef struct _z_fut_data_t { _z_fut_t _fut; _z_fut_schedule_t _schedule; } _z_fut_data_t; static inline void _z_fut_data_destroy(_z_fut_data_t *data) { _z_fut_destroy(&data->_fut); data->_schedule = _z_fut_schedule_ready(); // Reset status to ready after destroy } static inline void _z_fut_data_move(_z_fut_data_t *dst, _z_fut_data_t *src) { _z_fut_move(&dst->_fut, &src->_fut); dst->_schedule = src->_schedule; src->_schedule = _z_fut_schedule_ready(); } static inline size_t _z_size_fut_data_hmap_hash(const size_t *key) { return *key; } #ifndef _ZP_EXECUTOR_MAX_NUM_FUTURES #define _ZP_EXECUTOR_MAX_NUM_FUTURES 64 #endif #define _ZP_EXECUTOR_MAX_FUT_BUCKET_COUNT (_ZP_EXECUTOR_MAX_NUM_FUTURES * 3 / 2) // 0.66 load factor #define _ZP_HASHMAP_TEMPLATE_KEY_TYPE size_t #define _ZP_HASHMAP_TEMPLATE_VAL_TYPE _z_fut_data_t #define _ZP_HASHMAP_TEMPLATE_NAME _z_fut_data_hmap #define _ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME _z_size_fut_data_hmap_hash #define _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT _ZP_EXECUTOR_MAX_FUT_BUCKET_COUNT #define _ZP_HASHMAP_TEMPLATE_CAPACITY _ZP_EXECUTOR_MAX_NUM_FUTURES #define _ZP_HASHMAP_TEMPLATE_VAL_DESTROY_FN_NAME _z_fut_data_destroy #define _ZP_HASHMAP_TEMPLATE_VAL_MOVE_FN_NAME _z_fut_data_move #include "zenoh-pico/collections/hashmap_template.h" #define _ZP_DEQUE_TEMPLATE_ELEM_TYPE _z_fut_data_hmap_index_t #define _ZP_DEQUE_TEMPLATE_NAME _z_fut_data_hmap_index_deque #define _ZP_DEQUE_TEMPLATE_SIZE _ZP_EXECUTOR_MAX_NUM_FUTURES #include "zenoh-pico/collections/deque_template.h" // Compare two sleeping-task indices by their wake-up time stored in the hashmap. // The context is a pointer to the task hashmap, which provides the wake-up times. static inline int _z_sleeping_fut_idx_cmp(const _z_fut_data_hmap_index_t *a, const _z_fut_data_hmap_index_t *b, const _z_fut_data_hmap_t *tasks) { uint64_t ta = _z_fut_schedule_get_wake_up_time_ms(_z_fut_data_hmap_node_at((_z_fut_data_hmap_t *)tasks, *a)->val._schedule); uint64_t tb = _z_fut_schedule_get_wake_up_time_ms(_z_fut_data_hmap_node_at((_z_fut_data_hmap_t *)tasks, *b)->val._schedule); if (ta < tb) return -1; if (ta > tb) return 1; return 0; } #define _ZP_PQUEUE_TEMPLATE_ELEM_TYPE _z_fut_data_hmap_index_t #define _ZP_PQUEUE_TEMPLATE_NAME _z_sleeping_fut_pqueue #define _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE _z_fut_data_hmap_t #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME _z_sleeping_fut_idx_cmp #define _ZP_PQUEUE_TEMPLATE_SIZE _ZP_EXECUTOR_MAX_NUM_FUTURES #include "zenoh-pico/collections/pqueue_template.h" typedef struct _z_executor_t { _z_fut_data_hmap_index_deque_t _ready_tasks; _z_sleeping_fut_pqueue_t _sleeping_tasks; _z_fut_data_hmap_t _tasks; z_clock_t _epoch; size_t _next_fut_id; } _z_executor_t; static inline void _z_executor_null(_z_executor_t *executor) { executor->_ready_tasks = _z_fut_data_hmap_index_deque_new(); executor->_sleeping_tasks = _z_sleeping_fut_pqueue_new(); executor->_tasks = _z_fut_data_hmap_new(); executor->_next_fut_id = 0; // Set context after _tasks is initialised so the pointer is valid. _z_sleeping_fut_pqueue_set_ctx(&executor->_sleeping_tasks, &executor->_tasks); } static inline void _z_executor_init(_z_executor_t *executor) { _z_executor_null(executor); executor->_epoch = z_clock_now(); } static inline _z_executor_t _z_executor_new(void) { _z_executor_t executor; _z_executor_init(&executor); return executor; } static inline void _z_executor_destroy(_z_executor_t *executor) { _z_fut_data_hmap_index_deque_destroy(&executor->_ready_tasks); _z_sleeping_fut_pqueue_destroy(&executor->_sleeping_tasks); _z_fut_data_hmap_destroy(&executor->_tasks); } // Spawn a new future to be executed. // The executor takes the ownership of the future, and will destroy the future (by calling the destroy_fn) when the // future is finished or cancelled. The caller can use the future handle to cancel the future or check its status. _z_fut_handle_t _z_executor_spawn(_z_executor_t *executor, _z_fut_t *fut); typedef enum _z_executor_spin_result_status_t { _Z_EXECUTOR_SPIN_RESULT_NO_TASKS, _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK, _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT, _Z_EXECUTOR_SPIN_RESULT_FAILED, } _z_executor_spin_result_status_t; typedef struct _z_executor_spin_result_t { _z_executor_spin_result_status_t status; z_clock_t next_wake_up_time; } _z_executor_spin_result_t; _z_executor_spin_result_t _z_executor_spin(_z_executor_t *executor); _z_fut_status_t _z_executor_get_fut_status(const _z_executor_t *executor, const _z_fut_handle_t *handle); bool _z_executor_cancel_fut(_z_executor_t *executor, const _z_fut_handle_t *handle); bool _z_executor_resume_suspended_fut(_z_executor_t *executor, const _z_fut_handle_t *handle); #ifdef __cplusplus } #endif #endif ================================================ FILE: include/zenoh-pico/runtime/runtime.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_RUNTIME_RUNTIME_H #define ZENOH_PICO_RUNTIME_RUNTIME_H #include "zenoh-pico/config.h" #define _ZP_EXECUTOR_MAX_NUM_FUTURES Z_RUNTIME_MAX_TASKS #if Z_FEATURE_MULTI_THREAD == 1 #include "zenoh-pico/runtime/background_executor.h" #else #include "zenoh-pico/runtime/executor.h" #endif #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef _z_background_executor_t _z_runtime_t; static inline _z_fut_handle_t _z_runtime_spawn(_z_runtime_t *runtime, _z_fut_t *fut) { _z_fut_handle_t handle; _z_background_executor_spawn(runtime, fut, &handle); return handle; } static inline z_result_t _z_runtime_cancel_fut(_z_runtime_t *runtime, _z_fut_handle_t *handle) { return _z_background_executor_cancel_fut(runtime, handle); } static inline z_result_t _z_runtime_init(_z_runtime_t *runtime) { return _z_background_executor_init_deferred(runtime); } static inline void _z_runtime_clear(_z_runtime_t *runtime) { _z_background_executor_destroy(runtime); } static inline void _z_runtime_null(_z_runtime_t *runtime) { _z_background_executor_null(runtime); } static inline z_result_t _z_runtime_start(_z_runtime_t *runtime, z_task_attr_t *task_attr) { return _z_background_executor_start(runtime, task_attr); } static inline z_result_t _z_runtime_stop(_z_runtime_t *runtime) { return _z_background_executor_stop(runtime); } #else typedef _z_executor_t _z_runtime_t; static inline _z_fut_handle_t _z_runtime_spawn(_z_runtime_t *runtime, _z_fut_t *fut) { return _z_executor_spawn(runtime, fut); } static inline z_result_t _z_runtime_init(_z_runtime_t *runtime) { _z_executor_init(runtime); return _Z_RES_OK; } static inline void _z_runtime_clear(_z_runtime_t *runtime) { _z_executor_destroy(runtime); } static inline void _z_runtime_null(_z_runtime_t *runtime) { _z_executor_null(runtime); } static inline z_result_t _z_runtime_cancel_fut(_z_runtime_t *runtime, _z_fut_handle_t *handle) { _z_executor_cancel_fut(runtime, handle); return _Z_RES_OK; } static inline void _z_runtime_spin_once(_z_runtime_t *runtime) { _z_executor_spin(runtime); } static inline z_result_t _z_runtime_stop(_z_runtime_t *runtime) { _ZP_UNUSED(runtime); return _Z_RES_OK; } #endif #ifdef __cplusplus } // extern "C" #endif #endif ================================================ FILE: include/zenoh-pico/session/cancellation.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_CANCELLATION_H #define ZENOH_PICO_SESSION_CANCELLATION_H #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/sync_group.h" #include "zenoh-pico/system/platform.h" typedef z_result_t (*_z_cancellation_token_on_cancel_handler_callback)(void* arg); typedef void (*_z_cancellation_token_on_cancel_handler_dropper)(void* arg); typedef struct { _z_cancellation_token_on_cancel_handler_callback _on_cancel; _z_cancellation_token_on_cancel_handler_dropper _on_drop; void* _arg; } _z_cancellation_token_on_cancel_handler_t; z_result_t _z_cancellation_token_on_cancel_handler_call(_z_cancellation_token_on_cancel_handler_t* handler); static inline _z_cancellation_token_on_cancel_handler_t _z_cancellation_token_on_cancel_handler_null(void) { _z_cancellation_token_on_cancel_handler_t h = {0}; return h; } static inline void _z_cancellation_token_on_cancel_handler_drop(_z_cancellation_token_on_cancel_handler_t* h) { if (h->_on_drop != NULL) { h->_on_drop(h->_arg); } h->_on_cancel = NULL; h->_arg = NULL; h->_on_drop = NULL; } static inline void _z_cancellation_token_on_cancel_handler_move(_z_cancellation_token_on_cancel_handler_t* dst, _z_cancellation_token_on_cancel_handler_t* src) { *dst = *src; *src = _z_cancellation_token_on_cancel_handler_null(); } _Z_ELEM_DEFINE(_z_cancellation_token_on_cancel_handler, _z_cancellation_token_on_cancel_handler_t, _z_noop_size, _z_cancellation_token_on_cancel_handler_drop, _z_noop_copy, _z_cancellation_token_on_cancel_handler_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_cancellation_token_on_cancel_handler, _z_cancellation_token_on_cancel_handler_t) typedef struct { _z_cancellation_token_on_cancel_handler_intmap_t _handlers; size_t _next_handler_id; _z_sync_group_notifier_t _cancel_sync_notifier; } _z_cancellation_handlers_storage_t; typedef struct { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex; #endif _z_cancellation_handlers_storage_t _handlers; _z_sync_group_t _sync_group; z_result_t _cancel_result; } _z_cancellation_token_t; z_result_t _z_cancellation_token_create(_z_cancellation_token_t* ct); bool _z_cancellation_token_is_cancelled(const _z_cancellation_token_t* ct); z_result_t _z_cancellation_token_cancel(_z_cancellation_token_t* ct); z_result_t _z_cancellation_token_cancel_with_timeout(_z_cancellation_token_t* ct, uint32_t timeout_ms); void _z_cancellation_token_clear(_z_cancellation_token_t* ct); // if return value is not _Z_RES_OK, handler is not consumed z_result_t _z_cancellation_token_add_on_cancel_handler(_z_cancellation_token_t* ct, _z_cancellation_token_on_cancel_handler_t* handler, size_t* handler_id); z_result_t _z_cancellation_token_remove_on_cancel_handler(_z_cancellation_token_t* ct, size_t handler_id); z_result_t _z_cancellation_token_get_notifier(_z_cancellation_token_t* ct, _z_sync_group_notifier_t* notifier); _Z_REFCOUNT_DEFINE(_z_cancellation_token, _z_cancellation_token) #endif ================================================ FILE: include/zenoh-pico/session/interest.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_INTEREST_H #define ZENOH_PICO_SESSION_INTEREST_H #include #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_INTEREST == 1 _z_session_interest_rc_t *_z_get_interest_by_id(_z_session_t *zn, const _z_zint_t id); _z_session_interest_rc_t *_z_register_interest(_z_session_t *zn, _z_session_interest_t *intr); void _z_unregister_interest(_z_session_t *zn, _z_session_interest_rc_t *intr); #endif // Z_FEATURE_INTEREST == 1 void _z_interest_init(_z_session_t *zn); void _z_flush_interest(_z_session_t *zn); z_result_t _z_interest_process_declares(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer); z_result_t _z_interest_process_undeclares(_z_session_t *zn, const _z_declaration_t *decl, _z_transport_peer_common_t *peer); z_result_t _z_interest_process_declare_final(_z_session_t *zn, uint32_t id, _z_transport_peer_common_t *peer); z_result_t _z_interest_process_interest_final(_z_session_t *zn, uint32_t id); z_result_t _z_interest_process_interest(_z_session_t *zn, const _z_wireexpr_t *wireexpr, uint32_t id, uint8_t flags, _z_transport_peer_common_t *peer); z_result_t _z_interest_push_declarations_to_peer(_z_session_t *zn, _z_transport_peer_common_t *peer); z_result_t _z_interest_pull_resource_from_peers(_z_session_t *zn); void _z_interest_peer_disconnected(_z_session_t *zn, _z_transport_peer_common_t *peer); void _z_interest_replay_declare(_z_session_t *zn, _z_session_interest_t *interest); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_INTEREST_H */ ================================================ FILE: include/zenoh-pico/session/keyexpr.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #ifndef INCLUDE_ZENOH_PICO_SESSION_KEYEXPR_H #define INCLUDE_ZENOH_PICO_SESSION_KEYEXPR_H #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/weak_session.h" #ifdef __cplusplus extern "C" { #endif typedef struct { uint16_t _id; uint16_t _prefix_len; _z_session_weak_t _session; } _z_keyexpr_wire_declaration_t; static inline _z_keyexpr_wire_declaration_t _z_keyexpr_wire_declaration_null(void) { _z_keyexpr_wire_declaration_t d = {0}; return d; } static inline bool _z_keyexpr_wire_declaration_equals(const _z_keyexpr_wire_declaration_t *left, const _z_keyexpr_wire_declaration_t *right) { return left->_id == right->_id && left->_session._val == right->_session._val; } z_result_t _z_keyexpr_wire_declaration_new(_z_keyexpr_wire_declaration_t *declaration, const _z_string_t *keyexpr, const _z_session_rc_t *session); z_result_t _z_keyexpr_wire_declaration_undeclare(_z_keyexpr_wire_declaration_t *declaration); void _z_keyexpr_wire_declaration_clear(_z_keyexpr_wire_declaration_t *declaration); static inline bool _z_keyexpr_wire_declaration_is_declared_on_session(const _z_keyexpr_wire_declaration_t *declaration, const _z_session_t *s) { return !_Z_RC_IS_NULL(&declaration->_session) && declaration->_session._val == s; } _Z_REFCOUNT_DEFINE(_z_keyexpr_wire_declaration, _z_keyexpr_wire_declaration) typedef struct { _z_string_t _keyexpr; } _z_keyexpr_t; static inline _z_keyexpr_t _z_keyexpr_null(void) { _z_keyexpr_t ke = {0}; return ke; } bool _z_keyexpr_includes(const _z_keyexpr_t *left, const _z_keyexpr_t *right); bool _z_keyexpr_intersects(const _z_keyexpr_t *left, const _z_keyexpr_t *right); zp_keyexpr_canon_status_t _z_keyexpr_is_canon(const char *start, size_t len); zp_keyexpr_canon_status_t _z_keyexpr_canonize(char *start, size_t *len); z_result_t _z_keyexpr_concat(_z_keyexpr_t *key, const _z_keyexpr_t *left, const char *right, size_t len); z_result_t _z_keyexpr_join(_z_keyexpr_t *key, const _z_keyexpr_t *left, const _z_keyexpr_t *right); static inline _z_keyexpr_t _z_keyexpr_alias(const _z_keyexpr_t *src) { _z_keyexpr_t ret; ret._keyexpr = _z_string_alias(src->_keyexpr); return ret; } _z_keyexpr_t _z_keyexpr_alias_from_string(const _z_string_t *str); _z_keyexpr_t _z_keyexpr_alias_from_substr(const char *str, size_t len); static inline _z_keyexpr_t _z_keyexpr_alias_from_str(const char *str) { // SAFETY: By convention in pico code-base passing const char* without len implies that it is null-terminated. // Flawfinder: ignore [CWE-126] return _z_keyexpr_alias_from_substr(str, strlen(str)); } z_result_t _z_keyexpr_from_string(_z_keyexpr_t *dst, const _z_string_t *str); z_result_t _z_keyexpr_from_substr(_z_keyexpr_t *dst, const char *str, size_t len); static inline void _z_keyexpr_clear(_z_keyexpr_t *key) { _z_string_clear(&key->_keyexpr); } static inline bool _z_keyexpr_check(const _z_keyexpr_t *key) { return _z_string_check(&key->_keyexpr); } static inline z_result_t _z_keyexpr_copy(_z_keyexpr_t *dst, const _z_keyexpr_t *src) { *dst = _z_keyexpr_null(); return _z_string_copy(&dst->_keyexpr, &src->_keyexpr); } static inline z_result_t _z_keyexpr_move(_z_keyexpr_t *dst, _z_keyexpr_t *src) { *dst = _z_keyexpr_null(); _Z_CLEAN_RETURN_IF_ERR(_z_string_move(&dst->_keyexpr, &src->_keyexpr), _z_keyexpr_clear(src)); return _Z_RES_OK; } static inline _z_keyexpr_t _z_keyexpr_steal(_Z_MOVE(_z_keyexpr_t) src) { _z_keyexpr_t stolen = *src; *src = _z_keyexpr_null(); return stolen; } size_t _z_keyexpr_non_wild_prefix_len(const _z_keyexpr_t *key); static inline int _z_keyexpr_compare(const _z_keyexpr_t *first, const _z_keyexpr_t *second) { return _z_string_compare(&first->_keyexpr, &second->_keyexpr); } static inline bool _z_keyexpr_equals(const _z_keyexpr_t *first, const _z_keyexpr_t *second) { return _z_keyexpr_compare(first, second) == 0; } static inline size_t _z_keyexpr_size(_z_keyexpr_t *p) { _ZP_UNUSED(p); return sizeof(_z_keyexpr_t); } _z_wireexpr_t _z_keyexpr_alias_to_wire(const _z_keyexpr_t *key); typedef struct { _z_keyexpr_wire_declaration_rc_t _declaration; _z_keyexpr_t _inner; } _z_declared_keyexpr_t; static inline bool _z_declared_keyexpr_includes(const _z_declared_keyexpr_t *left, const _z_declared_keyexpr_t *right) { return _z_keyexpr_includes(&left->_inner, &right->_inner); } static inline bool _z_declared_keyexpr_intersects(const _z_declared_keyexpr_t *left, const _z_declared_keyexpr_t *right) { return _z_keyexpr_intersects(&left->_inner, &right->_inner); } z_result_t _z_declared_keyexpr_concat(_z_declared_keyexpr_t *key, const _z_declared_keyexpr_t *left, const char *right, size_t len); z_result_t _z_declared_keyexpr_join(_z_declared_keyexpr_t *key, const _z_declared_keyexpr_t *left, const _z_declared_keyexpr_t *right); static inline _z_declared_keyexpr_t _z_declared_keyexpr_null(void) { _z_declared_keyexpr_t ke = {0}; return ke; } _z_declared_keyexpr_t _z_declared_keyexpr_alias_from_string(const _z_string_t *str); _z_declared_keyexpr_t _z_declared_keyexpr_alias_from_substr(const char *str, size_t len); static inline _z_declared_keyexpr_t _z_declared_keyexpr_alias_from_str(const char *str) { // SAFETY: By convention in pico code-base passing const char* without len implies that it is null-terminated. // Flawfinder: ignore [CWE-126] return _z_declared_keyexpr_alias_from_substr(str, strlen(str)); } static inline z_result_t _z_declared_keyexpr_from_string(_z_declared_keyexpr_t *dst, const _z_string_t *str) { *dst = _z_declared_keyexpr_null(); return _z_keyexpr_from_string(&dst->_inner, str); } static inline z_result_t _z_declared_keyexpr_from_substr(_z_declared_keyexpr_t *dst, const char *str, size_t len) { *dst = _z_declared_keyexpr_null(); return _z_keyexpr_from_substr(&dst->_inner, str, len); } z_result_t _z_declared_keyexpr_copy(_z_declared_keyexpr_t *dst, const _z_declared_keyexpr_t *src); static inline _z_declared_keyexpr_t _z_declared_keyexpr_steal(_Z_MOVE(_z_declared_keyexpr_t) src) { _z_declared_keyexpr_t stolen = *src; *src = _z_declared_keyexpr_null(); return stolen; } static inline void _z_declared_keyexpr_clear(_z_declared_keyexpr_t *key) { _z_keyexpr_wire_declaration_rc_drop(&key->_declaration); _z_keyexpr_clear(&key->_inner); } static inline bool _z_declared_keyexpr_check(const _z_declared_keyexpr_t *key) { return _z_keyexpr_check(&key->_inner); } static inline bool _z_declared_keyexpr_is_fully_optimized(const _z_declared_keyexpr_t *key, const _z_session_t *session) { return !_Z_RC_IS_NULL(&key->_declaration) && _z_keyexpr_wire_declaration_is_declared_on_session(_Z_RC_IN_VAL(&key->_declaration), session) && _Z_RC_IN_VAL(&key->_declaration)->_prefix_len == _z_string_len(&key->_inner._keyexpr); } static inline bool _z_declared_keyexpr_is_non_wild_prefix_optimized(const _z_declared_keyexpr_t *key, const _z_session_t *session) { return !_Z_RC_IS_NULL(&key->_declaration) && _z_keyexpr_wire_declaration_is_declared_on_session(_Z_RC_IN_VAL(&key->_declaration), session) && _Z_RC_IN_VAL(&key->_declaration)->_prefix_len == _z_keyexpr_non_wild_prefix_len(&key->_inner); } static inline bool _z_declared_keyexpr_equals(const _z_declared_keyexpr_t *left, const _z_declared_keyexpr_t *right) { return _z_keyexpr_equals(&left->_inner, &right->_inner); } z_result_t _z_declared_keyexpr_move(_z_declared_keyexpr_t *dst, _z_declared_keyexpr_t *src); _z_wireexpr_t _z_declared_keyexpr_alias_to_wire(const _z_declared_keyexpr_t *key, const _z_session_t *session); z_result_t _z_declared_keyexpr_declare(const _z_session_rc_t *zs, _z_declared_keyexpr_t *out, const _z_declared_keyexpr_t *keyexpr); z_result_t _z_declared_keyexpr_declare_non_wild_prefix(const _z_session_rc_t *zs, _z_declared_keyexpr_t *out, const _z_declared_keyexpr_t *keyexpr); static inline size_t _z_declared_keyexpr_size(_z_declared_keyexpr_t *p) { _ZP_UNUSED(p); return sizeof(_z_declared_keyexpr_t); } #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_KEYEXPR_H */ ================================================ FILE: include/zenoh-pico/session/keyexpr_match_template.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_KEYEXPR_MATCH_TEMPLATE_H #define ZENOH_PICO_SESSION_KEYEXPR_MATCH_TEMPLATE_H #include #include #include #include "zenoh-pico/collections/cat.h" const char _Z_VERBATIM = '@'; const char _Z_DELIMITER = '/'; const char _Z_STAR = '*'; const char _Z_DSL0 = '$'; const char _Z_DSL1 = '*'; const size_t _Z_DELIMITER_LEN = 1; const size_t _Z_DSL_LEN = 2; const size_t _Z_DOUBLE_STAR_LEN = 2; typedef enum _z_chunk_match_result_t { _Z_CHUNK_MATCH_RESULT_NO, _Z_CHUNK_MATCH_RESULT_YES, _Z_CHUNK_MATCH_RESULT_LEFT_SUPERWILD, _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD, } _z_chunk_match_result_t; typedef struct _z_chunk_forward_match_data_t { _z_chunk_match_result_t result; const char *lend; // only valid if result is _Z_CHUNK_MATCH_RESULT_YES const char *rend; // only valid if result is _Z_CHUNK_MATCH_RESULT_YES } _z_chunk_forward_match_data_t; typedef struct _z_chunk_backward_match_data_t { _z_chunk_match_result_t result; const char *lbegin; // only valid if result is _Z_CHUNK_MATCH_RESULT_YES const char *rbegin; // only valid if result is _Z_CHUNK_MATCH_RESULT_YES } _z_chunk_backward_match_data_t; static inline const char *_z_chunk_end(const char *begin, const char *end) { const char *sep = (const char *)memchr(begin, _Z_DELIMITER, (size_t)(end - begin)); return sep ? sep : end; } static inline const char *_z_chunk_begin(const char *begin, const char *end) { const char *last = end - 1; while (begin <= last && *last != _Z_DELIMITER) { last--; } return last + 1; } static inline bool _z_keyexpr_is_double_star(const char *begin, const char *end) { size_t len = (size_t)(end - begin); return len == 2 && begin[0] == _Z_STAR && begin[1] == _Z_STAR; } static inline bool _z_chunk_contains_stardsl(const char *begin, const char *end) { return memchr(begin, _Z_DSL0, (size_t)(end - begin)) != NULL; } static bool _z_chunk_right_contains_all_stardsl_subchunks_of_left(const char *lbegin, const char *lend, const char *rbegin, const char *rend) { // Check if right chunk contains all stardsl subchunks of left chunk const char *next_dsl = NULL; bool matched = false; do { size_t llen = (size_t)(lend - lbegin); next_dsl = memchr(lbegin, _Z_DSL0, llen); size_t clen = next_dsl ? (size_t)(next_dsl - lbegin) : llen; // TODO: Should we consider a more optimial substring search algorithm, like Boyer-Moore ? The simple version // below looks good enough if the chunks are short. matched = false; while (rbegin + clen <= rend) { if (memcmp(rbegin, lbegin, clen) == 0) { rbegin += clen; matched = true; break; } rbegin++; } lbegin += clen + _Z_DSL_LEN; // skip the matched part and the following stardsl } while (matched && next_dsl != NULL); return matched; } static inline bool _z_chunk_is_stardsl(const char *begin, const char *kend) { return *begin == _Z_DSL0 && (begin + _Z_DSL_LEN == kend || *(begin + 2) == _Z_DELIMITER); } static inline bool _z_chunk_is_stardsl_backward(const char *kbegin, const char *last) { // does not test for ** return *last == _Z_DSL1 && (last == kbegin + 1 || (last > kbegin + 1 && *(last - 2) == _Z_DELIMITER)); } static inline const char *_z_keyexpr_get_next_double_star_chunk(const char *begin, const char *end) { while (begin + 1 < end) { const char *c = (const char *)memchr(begin, _Z_STAR, (size_t)(end - begin - 1)); if (c == NULL) { return NULL; } if (*(c + 1) == _Z_STAR) { return c; } begin = c + _Z_DOUBLE_STAR_LEN; } return NULL; } static inline const char *_z_keyexpr_get_next_verbatim_chunk(const char *begin, const char *end) { return (const char *)memchr(begin, _Z_VERBATIM, (size_t)(end - begin)); } #endif #ifndef _ZP_KE_MATCH_TEMPLATE_INTERSECTS #error "_ZP_KE_MATCH_TEMPLATE_INTERSECTS must be defined before including keyexpr_match_template.h" #endif #if _ZP_KE_MATCH_TEMPLATE_INTERSECTS == 1 #define _ZP_KE_MATCH_OP intersects #define _ZP_KE_MATCH_TYPE_INTERSECTS true #define _ZP_KE_MATCH_TYPE_INCLUDES false #else #define _ZP_KE_MATCH_OP includes #define _ZP_KE_MATCH_TYPE_INTERSECTS false #define _ZP_KE_MATCH_TYPE_INCLUDES true #endif bool _ZP_CAT(_z_chunk_special, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend) { // left is a non-empty part of a chunk following a stardsl and preceeding a stardsl i.e. $*lllll$*, // right is chunk that does not start, nor end with a stardsl(s), but can contain stardsl in the middle, // original left chunk has the following form $*L1$*L2$*...$*LN$* // so there are only 2 cases where it can intersect with right: // 1. right contains a stardsl in the middle, in this case bound stardsls in left can match parts before and after // stardsl in the right, while right's stardsl can match the middle part of left. // // 2. every subchunk of left (L1, L2, ..., LN) is present in right in the // correct order (and they do not overlap). // // For includes relation, only case 2 is valid. return (_ZP_KE_MATCH_TYPE_INTERSECTS && _z_chunk_contains_stardsl(rbegin, rend)) || _z_chunk_right_contains_all_stardsl_subchunks_of_left(lbegin, lend, rbegin, rend); } _z_chunk_forward_match_data_t _ZP_CAT(_z_chunk_forward_backward, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lkend, const char *rbegin, const char *rkend) { // left is a part of a chunk following a stardsl. i.e $*llllll _z_chunk_forward_match_data_t result = {0}; result.lend = _z_chunk_end(lbegin, lkend); result.rend = _z_chunk_end(rbegin, rkend); const char *lend = result.lend; const char *rend = result.rend; while (lend > lbegin && rend > rbegin) { lend--; rend--; if (_ZP_KE_MATCH_TYPE_INTERSECTS && *rend == _Z_DSL1) { result.result = _Z_CHUNK_MATCH_RESULT_YES; return result; } else if (*lend == _Z_DSL1) { result.result = _ZP_CAT(_z_chunk_special, _ZP_KE_MATCH_OP)(lbegin, lend + 1 - _Z_DSL_LEN, rbegin, rend + 1) ? _Z_CHUNK_MATCH_RESULT_YES : _Z_CHUNK_MATCH_RESULT_NO; return result; } else if (*lend != *rend) { result.result = _Z_CHUNK_MATCH_RESULT_NO; return result; } } if (lbegin == lend) { // remainder of left is entirely matched, leading stardsl can match the rest of right result.result = _Z_CHUNK_MATCH_RESULT_YES; } // if rbegin == rend remainder of right is entirely matched, but there are still chars in left (which are not // strardsl due to canonicalization), so no match return result; } _z_chunk_forward_match_data_t _ZP_CAT(_z_chunk_forward, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lkend, const char *rbegin, const char *rkend) { // left and right are guaranteed to be non-empty canonized chunks _z_chunk_forward_match_data_t result = {0}; // assume canonized chunks bool left_is_wild = *lbegin == _Z_STAR; bool right_is_wild = *rbegin == _Z_STAR; bool left_is_superwild = left_is_wild && (lbegin + 2 <= lkend) && *(lbegin + 1) == _Z_STAR; bool right_is_superwild = right_is_wild && (rbegin + 2 <= rkend) && *(rbegin + 1) == _Z_STAR; if (left_is_superwild) { result.result = _Z_CHUNK_MATCH_RESULT_LEFT_SUPERWILD; return result; } else if (right_is_superwild) { if (_ZP_KE_MATCH_TYPE_INCLUDES) { return result; } result.result = _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD; return result; } bool left_is_verbatim = *lbegin == _Z_VERBATIM; bool right_is_verbatim = *rbegin == _Z_VERBATIM; if (left_is_verbatim != right_is_verbatim) { return result; } else if (left_is_wild) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lend = lbegin + 1; result.rend = _z_chunk_end(rbegin, rkend); return result; } else if (right_is_wild) { if (_ZP_KE_MATCH_TYPE_INCLUDES) { return result; } result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lend = _z_chunk_end(lbegin, lkend); result.rend = rbegin + 1; return result; } // at this stage we should only care about stardsl, as the presence of verbatim or wild is already checked. while (lbegin < lkend && rbegin < rkend && *lbegin != _Z_DELIMITER && *rbegin != _Z_DELIMITER) { if (*lbegin == _Z_DSL0) { return _ZP_CAT(_z_chunk_forward_backward, _ZP_KE_MATCH_OP)(lbegin + _Z_DSL_LEN, lkend, rbegin, rkend); } else if (_ZP_KE_MATCH_TYPE_INTERSECTS && *rbegin == _Z_DSL0) { _z_chunk_forward_match_data_t res = _ZP_CAT(_z_chunk_forward_backward, _ZP_KE_MATCH_OP)(rbegin + _Z_DSL_LEN, rkend, lbegin, lkend); result.lend = res.rend; result.rend = res.lend; result.result = res.result; return result; } else if (*lbegin != *rbegin) { return result; } lbegin++; rbegin++; } bool left_exhausted = lbegin == lkend || *lbegin == _Z_DELIMITER; bool right_exhausted = rbegin == rkend || *rbegin == _Z_DELIMITER; if (left_exhausted && right_exhausted) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lend = lbegin; result.rend = rbegin; } else if (_ZP_KE_MATCH_TYPE_INTERSECTS && left_exhausted && _z_chunk_is_stardsl(rbegin, rkend)) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lend = lbegin; result.rend = rbegin + _Z_DSL_LEN; } else if (right_exhausted && _z_chunk_is_stardsl(lbegin, lkend)) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lend = lbegin + _Z_DSL_LEN; result.rend = rbegin; } return result; } _z_chunk_backward_match_data_t _ZP_CAT(_z_chunk_backward_forward, _ZP_KE_MATCH_OP)(const char *lkbegin, const char *lend, const char *rkbegin, const char *rend) { // left is a part of a chunk preceeding a stardsl i.e lllll$* _z_chunk_backward_match_data_t result = {0}; const char *lbegin = _z_chunk_begin(lkbegin, lend); const char *rbegin = _z_chunk_begin(rkbegin, rend); if ((*lbegin == _Z_VERBATIM) != (*rbegin == _Z_VERBATIM)) { // one is verbatim, the other is not, so no match return result; } result.lbegin = lbegin; result.rbegin = rbegin; // chunks can not be star, nor doublestar while (lbegin < lend && rbegin < rend) { if (_ZP_KE_MATCH_TYPE_INTERSECTS && *rbegin == _Z_DSL0) { result.result = _Z_CHUNK_MATCH_RESULT_YES; return result; } else if (*lbegin == _Z_DSL0) { result.result = _ZP_CAT(_z_chunk_special, _ZP_KE_MATCH_OP)(lbegin + 2, lend, rbegin, rend) ? _Z_CHUNK_MATCH_RESULT_YES : _Z_CHUNK_MATCH_RESULT_NO; return result; } else if (*lbegin != *rbegin) { return result; } lbegin++; rbegin++; } if (lbegin == lend) { // remainder of left is entirely matched, suffix stardsl can match the rest of right result.result = _Z_CHUNK_MATCH_RESULT_YES; } return result; } _z_chunk_backward_match_data_t _ZP_CAT(_z_chunk_backward, _ZP_KE_MATCH_OP)(const char *lkbegin, const char *lend, const char *rkbegin, const char *rend) { _z_chunk_backward_match_data_t result = {0}; const char *llast = lend - 1; const char *rlast = rend - 1; // assume canonized chunks bool left_is_wild = *llast == _Z_STAR; bool right_is_wild = *rlast == _Z_STAR; bool left_is_superwild = left_is_wild && (lkbegin + 2 <= lend) && *(llast - 1) == _Z_STAR; bool right_is_superwild = right_is_wild && (rkbegin + 2 <= rend) && *(rlast - 1) == _Z_STAR; if (left_is_superwild) { result.result = _Z_CHUNK_MATCH_RESULT_LEFT_SUPERWILD; return result; } else if (right_is_superwild) { if (_ZP_KE_MATCH_TYPE_INCLUDES) { return result; } result.result = _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD; return result; } left_is_wild = left_is_wild && (lkbegin == llast || *(llast - 1) == _Z_DELIMITER); right_is_wild = right_is_wild && (rkbegin == rlast || *(rlast - 1) == _Z_DELIMITER); if (left_is_wild) { result.lbegin = llast; result.rbegin = _z_chunk_begin(rkbegin, rend); result.result = *result.rbegin == _Z_VERBATIM ? _Z_CHUNK_MATCH_RESULT_NO : _Z_CHUNK_MATCH_RESULT_YES; return result; } else if (right_is_wild) { if (_ZP_KE_MATCH_TYPE_INCLUDES) { return result; } result.lbegin = _z_chunk_begin(lkbegin, lend); result.rbegin = rlast; result.result = *result.lbegin == _Z_VERBATIM ? _Z_CHUNK_MATCH_RESULT_NO : _Z_CHUNK_MATCH_RESULT_YES; return result; } while (llast >= lkbegin && rlast >= rkbegin && *llast != _Z_DELIMITER && *rlast != _Z_DELIMITER) { if (*llast == _Z_DSL1) { return _ZP_CAT(_z_chunk_backward_forward, _ZP_KE_MATCH_OP)(lkbegin, llast + 1 - _Z_DSL_LEN, rkbegin, rlast + 1); } else if (_ZP_KE_MATCH_TYPE_INTERSECTS && *rlast == _Z_DSL1) { _z_chunk_backward_match_data_t res = _ZP_CAT(_z_chunk_backward_forward, _ZP_KE_MATCH_OP)( rkbegin, rlast + 1 - _Z_DSL_LEN, lkbegin, llast + 1); result.lbegin = res.rbegin; result.rbegin = res.lbegin; result.result = res.result; return result; } else if (*llast != *rlast) { return result; } llast--; rlast--; } bool left_exhausted = lkbegin == llast + 1 || *llast == _Z_DELIMITER; bool right_exhausted = rkbegin == rlast + 1 || *rlast == _Z_DELIMITER; if (left_exhausted && right_exhausted) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lbegin = llast + 1; result.rbegin = rlast + 1; } else if (_ZP_KE_MATCH_TYPE_INTERSECTS && left_exhausted && _z_chunk_is_stardsl_backward(rkbegin, rlast)) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lbegin = llast + 1; result.rbegin = rlast + 1 - _Z_DSL_LEN; } else if (right_exhausted && _z_chunk_is_stardsl_backward(lkbegin, llast)) { result.result = _Z_CHUNK_MATCH_RESULT_YES; result.lbegin = llast + 1 - _Z_DSL_LEN; result.rbegin = rlast + 1; } return result; } bool _ZP_CAT(_z_keyexpr_special, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend) { // left is a non-empty part of a ke sorrounded by doublestar i.e. **/l/l/l/l** // right is ke that does not start, nor end with a doublestar(s), but can contain doublestar in the middle // none of the kes contain any verbatim chunks // so there are only 2 cases where left can intersect with right: // 1. right contains a doublestar in the middle, in this case bound doublestars in left can match parts before and // after doublestar in right, while right's doublestar can match the middle part of left. // // 2. every chunk of left **/L1/**/L2/**/../LN/** is matched in right // in the correct order and without overlap. // For includes relation, only case 2 is valid. if (_ZP_KE_MATCH_TYPE_INTERSECTS && _z_keyexpr_get_next_double_star_chunk(rbegin, rend) != NULL) { return true; } // right is guaranteed not to have doublestars for intersects, while for includes we do not care while (lbegin < lend) { const char *lcbegin = lbegin; const char *lcend = _z_keyexpr_get_next_double_star_chunk(lbegin, lend); lcend = lcend != NULL ? lcend - _Z_DELIMITER_LEN : lend; const char *rcbegin = rbegin; while (lcbegin < lcend) { if (rcbegin >= rend) { return false; } _z_chunk_forward_match_data_t res = _ZP_CAT(_z_chunk_forward, _ZP_KE_MATCH_OP)(lcbegin, lcend, rcbegin, rend); if (res.result == _Z_CHUNK_MATCH_RESULT_YES) { lcbegin = res.lend + _Z_DELIMITER_LEN; rcbegin = res.rend + _Z_DELIMITER_LEN; } else { // the only other possible case is res.result == _Z_CHUNK_MATCH_RESULT_NO // reset subke matching and try to match current left subke with the next right chunk lcbegin = lbegin; rbegin = _z_chunk_end(rbegin, rend) + _Z_DELIMITER_LEN; rcbegin = rbegin; } } lbegin = lcbegin; rbegin = rcbegin; } return true; } bool _ZP_CAT(_z_keyexpr_parts, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend); bool _ZP_CAT(_z_keyexpr_backward, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend, bool can_have_verbatim) { // left is a suffix following a doublestar i.e. **/l/l/l/l while (lbegin < lend && rbegin < rend) { _z_chunk_backward_match_data_t res = _ZP_CAT(_z_chunk_backward, _ZP_KE_MATCH_OP)(lbegin, lend, rbegin, rend); if (res.result == _Z_CHUNK_MATCH_RESULT_NO || (_ZP_KE_MATCH_TYPE_INCLUDES && res.result == _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD)) { return false; } else if (res.result == _Z_CHUNK_MATCH_RESULT_YES) { bool left_exhausted = lbegin == res.lbegin; bool right_exhausted = rbegin == res.rbegin; if (left_exhausted && right_exhausted) { return true; // leading doublestar in left matches an empty string } else if (left_exhausted) { // left is exhausted, right is matched if it does not contain any verbatim chunks return !can_have_verbatim || _z_keyexpr_get_next_verbatim_chunk(rbegin, res.rbegin - _Z_DELIMITER_LEN) == NULL; } else if (right_exhausted) { return false; // right is exhausted, left is not (and thus contains at least one non doublestar chunk), // match is impossible } lend = res.lbegin - _Z_DELIMITER_LEN; rend = res.rbegin - _Z_DELIMITER_LEN; } else if (can_have_verbatim) { // Now this becomes more complicated. // In the absence of verbatim chunks, we could apply the same logic as for matching stardsl chunks, but not // here. Given that we would anyway need to scan both kes for verbatim chunks and compare them, we rather // fallback to splitting kes across verbatim chunks boundary and run matching on each subke. return _ZP_CAT(_z_keyexpr_parts, _ZP_KE_MATCH_OP)(lbegin - _Z_DOUBLE_STAR_LEN - _Z_DELIMITER_LEN, lend, rbegin, rend); } else if (res.result == _Z_CHUNK_MATCH_RESULT_LEFT_SUPERWILD) { return _ZP_CAT(_z_keyexpr_special, _ZP_KE_MATCH_OP)(lbegin, lend - _Z_DOUBLE_STAR_LEN - _Z_DELIMITER_LEN, rbegin, rend); } else if (res.result == _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD) { return true; } } bool left_exhausted = lbegin >= lend; bool right_exhausted = rbegin >= rend; return (left_exhausted || _z_keyexpr_is_double_star(lbegin, lend)) && (right_exhausted || (_ZP_KE_MATCH_TYPE_INTERSECTS && _z_keyexpr_is_double_star(rbegin, rend))); } bool _ZP_CAT(_z_keyexpr_forward, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend, bool can_have_verbatim) { while (lbegin < lend && rbegin < rend) { _z_chunk_forward_match_data_t res = _ZP_CAT(_z_chunk_forward, _ZP_KE_MATCH_OP)(lbegin, lend, rbegin, rend); if (res.result == _Z_CHUNK_MATCH_RESULT_YES) { lbegin = res.lend + _Z_DELIMITER_LEN; rbegin = res.rend + _Z_DELIMITER_LEN; } else if (res.result == _Z_CHUNK_MATCH_RESULT_LEFT_SUPERWILD) { if (lbegin + _Z_DOUBLE_STAR_LEN == lend) { // left is exhausted return _z_keyexpr_get_next_verbatim_chunk(rbegin, rend) == NULL; } return _ZP_CAT(_z_keyexpr_backward, _ZP_KE_MATCH_OP)(lbegin + _Z_DOUBLE_STAR_LEN + _Z_DELIMITER_LEN, lend, rbegin, rend, can_have_verbatim); } else if (_ZP_KE_MATCH_TYPE_INTERSECTS && res.result == _Z_CHUNK_MATCH_RESULT_RIGHT_SUPERWILD) { if (rbegin + _Z_DOUBLE_STAR_LEN == rend) { // right is exhausted return _z_keyexpr_get_next_verbatim_chunk(lbegin, lend) == NULL; } return _ZP_CAT(_z_keyexpr_backward, _ZP_KE_MATCH_OP)(rbegin + _Z_DOUBLE_STAR_LEN + _Z_DELIMITER_LEN, rend, lbegin, lend, can_have_verbatim); } else { return false; } } bool left_exhausted = lbegin >= lend; bool right_exhausted = rbegin >= rend; return (left_exhausted || _z_keyexpr_is_double_star(lbegin, lend)) && (right_exhausted || (_ZP_KE_MATCH_TYPE_INTERSECTS && _z_keyexpr_is_double_star(rbegin, rend))); } bool _ZP_CAT(_z_keyexpr_parts, _ZP_KE_MATCH_OP)(const char *lbegin, const char *lend, const char *rbegin, const char *rend) { while (lbegin < lend && rbegin < rend) { const char *lverbatim = _z_keyexpr_get_next_verbatim_chunk(lbegin, lend); const char *rverbatim = _z_keyexpr_get_next_verbatim_chunk(rbegin, rend); if (lverbatim == NULL && rverbatim == NULL) { return _ZP_CAT(_z_keyexpr_forward, _ZP_KE_MATCH_OP)(lbegin, lend, rbegin, rend, false); } else if (lverbatim == NULL || rverbatim == NULL) { // different number of verbatim chunks, kes can not match return false; } const char *lskend = lverbatim == lbegin ? lbegin : lverbatim - _Z_DELIMITER_LEN; const char *rskend = rverbatim == rbegin ? rbegin : rverbatim - _Z_DELIMITER_LEN; if (!_ZP_CAT(_z_keyexpr_forward, _ZP_KE_MATCH_OP)(lbegin, lskend, rbegin, rskend, false)) { return false; // prefixes before verbatim chunks do not match, so kes can not match } _z_chunk_forward_match_data_t res = _ZP_CAT(_z_chunk_forward, _ZP_KE_MATCH_OP)(lverbatim, lend, rverbatim, rend); if (res.result != _Z_CHUNK_MATCH_RESULT_YES) { return false; // verbatim chunks do not match, so kes can not match } lbegin = res.lend + _Z_DELIMITER_LEN; rbegin = res.rend + _Z_DELIMITER_LEN; } bool left_exhausted = lbegin >= lend; bool right_exhausted = rbegin >= rend; return (left_exhausted || _z_keyexpr_is_double_star(lbegin, lend)) && (right_exhausted || (_ZP_KE_MATCH_TYPE_INTERSECTS && _z_keyexpr_is_double_star(rbegin, rend))); } #undef _ZP_KE_MATCH_OP #undef _ZP_KE_MATCH_TYPE_INTERSECTS #undef _ZP_KE_MATCH_TYPE_INCLUDES #undef _ZP_KE_MATCH_TEMPLATE_INTERSECTS ================================================ FILE: include/zenoh-pico/session/liveliness.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_LIVELINESS_H #define ZENOH_PICO_SESSION_LIVELINESS_H #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif // Forward declaration to avoid cyclical include typedef struct _z_session_t _z_session_t; #if Z_FEATURE_LIVELINESS == 1 typedef struct { _z_keyexpr_t _key; uint32_t _id; _z_closure_reply_callback_t _callback; _z_drop_handler_t _dropper; void *_arg; #ifdef Z_FEATURE_UNSTABLE_API _z_pending_query_cancellation_data_t _cancellation_data; #endif } _z_liveliness_pending_query_t; static inline _z_liveliness_pending_query_t _z_liveliness_pending_query_null(void) { _z_liveliness_pending_query_t pq = {0}; return pq; } void _z_liveliness_pending_query_clear(_z_liveliness_pending_query_t *res); _Z_ELEM_DEFINE(_z_liveliness_pending_query, _z_liveliness_pending_query_t, _z_noop_size, _z_liveliness_pending_query_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_liveliness_pending_query, _z_liveliness_pending_query_t) #if Z_FEATURE_SUBSCRIPTION == 1 z_result_t _z_liveliness_process_remote_token_declare(_z_session_t *zn, uint32_t id, const _z_wireexpr_t *keyexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer); z_result_t _z_liveliness_process_remote_token_undeclare(_z_session_t *zn, uint32_t id, const _z_timestamp_t *timestamp); z_result_t _z_liveliness_subscription_undeclare_all(_z_session_t *zn); #endif #if Z_FEATURE_QUERY == 1 _z_liveliness_pending_query_t *_z_unsafe_liveliness_register_pending_query(_z_session_t *zn); z_result_t _z_liveliness_unregister_pending_query(_z_session_t *zn, uint32_t id); #endif void _z_liveliness_init(_z_session_t *zn); void _z_liveliness_clear(_z_session_t *zn); #endif // Z_FEATURE_LIVELINESS == 1 z_result_t _z_liveliness_process_token_declare(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer); z_result_t _z_liveliness_process_token_undeclare(_z_session_t *zn, const _z_n_msg_declare_t *decl); z_result_t _z_liveliness_process_declare_final(_z_session_t *zn, const _z_n_msg_declare_t *decl); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_LIVELINESS_H */ ================================================ FILE: include/zenoh-pico/session/loopback.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_LOOPBACK_H #define ZENOH_PICO_SESSION_LOOPBACK_H #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_session_deliver_push_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info); z_result_t _z_session_deliver_query_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_slice_t *parameters, z_consolidation_mode_t consolidation, _z_bytes_t *payload, _z_encoding_t *encoding, _z_bytes_t *attachment, const _z_source_info_t *source_info, _z_zint_t qid, uint64_t timeout_ms, _z_n_qos_t qos, bool implicit_anyke); z_result_t _z_session_deliver_reply_locally(const _z_query_t *query, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, const _z_source_info_t *source_info); z_result_t _z_session_deliver_reply_err_locally(const _z_query_t *query, const _z_session_rc_t *zn, _z_bytes_t *payload, _z_encoding_t *encoding, _z_n_qos_t qos); z_result_t _z_session_deliver_reply_final_locally(_z_session_t *zn, _z_zint_t rid); #if defined(Z_TEST_HOOKS) typedef _z_transport_common_t *(*_z_session_transport_override_fn)(_z_session_t *); void _z_session_set_transport_common_override(_z_session_transport_override_fn fn); typedef z_result_t (*_z_session_send_override_fn)(_z_session_t *zn, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl, void *peer, bool *handled); void _z_transport_set_send_n_msg_override(_z_session_send_override_fn fn); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_LOOPBACK_H */ ================================================ FILE: include/zenoh-pico/session/matching.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_MATCHING_H #define INCLUDE_ZENOH_PICO_SESSION_MATCHING_H #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif typedef struct { bool matching; // true if there exist matching Zenoh entities, false otherwise. } _z_matching_status_t; typedef void (*_z_closure_matching_status_callback_t)(const _z_matching_status_t *status, void *arg); typedef struct { void *context; _z_closure_matching_status_callback_t call; _z_drop_handler_t drop; } _z_closure_matching_status_t; #if Z_FEATURE_MATCHING == 1 static inline void _z_closure_matching_status_clear(_z_closure_matching_status_t *closure) { if (closure->drop != NULL) { closure->drop(closure->context); } } _Z_ELEM_DEFINE(_z_closure_matching_status, _z_closure_matching_status_t, _z_noop_size, _z_closure_matching_status_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_closure_matching_status, _z_closure_matching_status_t) #endif // Z_FEATURE_MATCHING == 1 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_MATCHING_H */ ================================================ FILE: include/zenoh-pico/session/push.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/message.h" #ifndef ZENOH_PICO_SESSION_PUSH_H #define ZENOH_PICO_SESSION_PUSH_H #ifdef __cplusplus extern "C" { #endif z_result_t _z_trigger_push(_z_session_t *zn, _z_n_msg_push_t *push, z_reliability_t reliability, _z_transport_peer_common_t *peer); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_PUSH_H */ ================================================ FILE: include/zenoh-pico/session/query.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_QUERY_H #define ZENOH_PICO_SESSION_QUERY_H #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/runtime/runtime.h" #ifdef __cplusplus extern "C" { #endif void _z_pending_query_process_timeout(_z_session_t *zn); _z_fut_fn_result_t _z_pending_query_process_timeout_task_fn(void *session_arg, _z_executor_t *executor); #if Z_FEATURE_QUERY == 1 /*------------------ Query ------------------*/ _z_pending_query_t *_z_unsafe_register_pending_query(_z_session_t *zn); z_result_t _z_trigger_query_reply_partial(_z_session_t *zn, _z_zint_t reply_context, _z_wireexpr_t *wireexpr, _z_msg_put_t *msg, z_sample_kind_t kind, _z_entity_global_id_t *replier_id, _z_transport_peer_common_t *peer); z_result_t _z_trigger_query_reply_err(_z_session_t *zn, _z_zint_t id, _z_msg_err_t *msg, _z_entity_global_id_t *replier_id); z_result_t _z_trigger_query_reply_final(_z_session_t *zn, _z_zint_t id); void _z_unregister_pending_query(_z_session_t *zn, _z_zint_t query_id); void _z_unregister_pending_queries_from_querier(_z_session_t *zn, uint32_t querier_id); void _z_flush_pending_queries(_z_session_t *zn); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_QUERY_H */ ================================================ FILE: include/zenoh-pico/session/queryable.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SESSION_QUERYABLE_H #define ZENOH_PICO_SESSION_QUERYABLE_H #include #include #include "zenoh-pico/collections/lru_cache.h" // Forward declaration to avoid cyclical include typedef struct _z_session_t _z_session_t; typedef struct _z_session_rc_t _z_session_rc_t; // Queryable infos _Z_SVEC_DEFINE(_z_session_queryable_rc, _z_session_queryable_rc_t) _Z_REFCOUNT_DEFINE(_z_session_queryable_rc_svec, _z_session_queryable_rc_svec) typedef struct { _z_keyexpr_t ke; _z_session_queryable_rc_svec_rc_t infos; bool is_remote; } _z_queryable_cache_data_t; void _z_unsafe_queryable_cache_invalidate(_z_session_t *zn); int _z_queryable_cache_data_compare(const void *first, const void *second); void _z_queryable_cache_data_clear(_z_queryable_cache_data_t *val); #if Z_FEATURE_QUERYABLE == 1 #define _Z_QUERYABLE_COMPLETE_DEFAULT false #define _Z_QUERYABLE_DISTANCE_DEFAULT 0 #if Z_FEATURE_RX_CACHE == 1 _Z_ELEM_DEFINE(_z_queryable, _z_queryable_cache_data_t, _z_noop_size, _z_queryable_cache_data_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_LRU_CACHE_DEFINE(_z_queryable, _z_queryable_cache_data_t, _z_queryable_cache_data_compare) #endif /*------------------ Queryable ------------------*/ _z_session_queryable_rc_t _z_get_session_queryable_by_id(_z_session_t *zn, const _z_zint_t id); _z_session_queryable_rc_t _z_register_session_queryable(_z_session_t *zn, _z_session_queryable_t *q); z_result_t _z_trigger_queryables(_z_transport_common_t *transport, _z_msg_query_t *query, _z_wireexpr_t *q_key, uint32_t qid, _z_n_qos_t qos, _z_transport_peer_common_t *peer); void _z_unregister_session_queryable(_z_session_t *zn, _z_session_queryable_rc_t *q); void _z_flush_session_queryable(_z_session_t *zn); #endif #endif /* ZENOH_PICO_SESSION_QUERYABLE_H */ ================================================ FILE: include/zenoh-pico/session/reply.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/definitions/network.h" #ifdef __cplusplus extern "C" { #endif #ifndef ZENOH_PICO_SESSION_REPLY_H #define ZENOH_PICO_SESSION_REPLY_H z_result_t _z_trigger_reply_partial(_z_session_t *zn, _z_zint_t id, _z_wireexpr_t *wireexpr, _z_msg_reply_t *reply, _z_entity_global_id_t *replier_id, _z_transport_peer_common_t *peer); z_result_t _z_trigger_reply_err(_z_session_t *zn, _z_zint_t id, _z_msg_err_t *error, _z_entity_global_id_t *replier_id); z_result_t _z_trigger_reply_final(_z_session_t *zn, _z_n_msg_response_final_t *final); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SESSION_REPLY_H */ ================================================ FILE: include/zenoh-pico/session/resource.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_RESOURCE_H #define INCLUDE_ZENOH_PICO_SESSION_RESOURCE_H #include #include "zenoh-pico/net/session.h" #ifdef __cplusplus extern "C" { #endif /*------------------ Entity ------------------*/ uint32_t _z_get_entity_id(_z_session_t *zn); /*------------------ Resource ------------------*/ uint16_t _z_get_resource_id(_z_session_t *zn); z_result_t _z_get_keyexpr_from_wireexpr(_z_session_t *zn, _z_keyexpr_t *out, const _z_wireexpr_t *expr, _z_transport_peer_common_t *peer, bool alias_wireexpr_if_possible); z_result_t _z_register_resource(_z_session_t *zn, const _z_wireexpr_t *key, uint16_t id, _z_transport_peer_common_t *peer, uint16_t *out_id); z_result_t _z_unregister_resource(_z_session_t *zn, uint16_t id, _z_transport_peer_common_t *peer); void _z_flush_local_resources(_z_session_t *zn); #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_RESOURCE_H */ ================================================ FILE: include/zenoh-pico/session/session.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_SESSION_H #define INCLUDE_ZENOH_PICO_SESSION_SESSION_H #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/list.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/cancellation.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/transport/manager.h" #ifdef __cplusplus extern "C" { #endif /** * The callback signature of the cleanup functions. */ typedef void (*_z_drop_handler_t)(void *arg); static inline void _z_drop_handler_execute(_z_drop_handler_t dropper, void *arg) { if (dropper != NULL) { dropper(arg); } } typedef enum { _Z_SUBSCRIBER_KIND_SUBSCRIBER = 0, _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER = 1, } _z_subscriber_kind_t; typedef struct { _z_keyexpr_t _key; uint16_t _id; uint16_t _refcount; } _z_resource_t; bool _z_resource_eq(const _z_resource_t *one, const _z_resource_t *two); void _z_resource_clear(_z_resource_t *res); void _z_resource_copy(_z_resource_t *dst, const _z_resource_t *src); void _z_resource_free(_z_resource_t **res); size_t _z_resource_size(_z_resource_t *p); _Z_ELEM_DEFINE(_z_resource, _z_resource_t, _z_resource_size, _z_resource_clear, _z_resource_copy, _z_noop_move, _z_resource_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_resource, _z_resource_t, true) _Z_ELEM_DEFINE(_z_keyexpr, _z_keyexpr_t, _z_keyexpr_size, _z_keyexpr_clear, _z_keyexpr_copy, _z_keyexpr_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_keyexpr, _z_keyexpr_t) _Z_SLIST_DEFINE(_z_keyexpr, _z_keyexpr_t, true) _Z_ELEM_DEFINE(_z_declared_keyexpr, _z_declared_keyexpr_t, _z_declared_keyexpr_size, _z_declared_keyexpr_clear, _z_declared_keyexpr_copy, _z_declared_keyexpr_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_INT_MAP_DEFINE(_z_declared_keyexpr, _z_declared_keyexpr_t) // Forward declaration to avoid cyclical include typedef struct _z_sample_t _z_sample_t; /** * The callback signature of the functions handling data messages. */ typedef void (*_z_closure_sample_callback_t)(_z_sample_t *sample, void *arg); typedef struct { _z_declared_keyexpr_t _key; uint32_t _id; z_locality_t _allowed_origin; _z_closure_sample_callback_t _callback; _z_drop_handler_t _dropper; void *_arg; _z_sync_group_notifier_t _session_callback_drop_notifier; _z_sync_group_notifier_t _subscriber_callback_drop_notifier; } _z_subscription_t; bool _z_subscription_eq(const _z_subscription_t *one, const _z_subscription_t *two); void _z_subscription_clear(_z_subscription_t *sub); static inline _z_subscription_t _z_subscription_null(void) { _z_subscription_t s = {0}; return s; } _Z_REFCOUNT_DEFINE(_z_subscription, _z_subscription) _Z_ELEM_DEFINE(_z_subscriber, _z_subscription_t, _z_noop_size, _z_subscription_clear, _z_noop_copy, _z_noop_move, _z_subscription_eq, _z_noop_cmp, _z_noop_hash) _Z_ELEM_DEFINE(_z_subscription_rc, _z_subscription_rc_t, _z_subscription_rc_size, _z_subscription_rc_drop, _z_subscription_rc_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_subscription_rc, _z_subscription_rc_t, true) typedef struct { _z_keyexpr_t _key; uint32_t _id; } _z_publication_t; // Forward type declaration to avoid cyclical include typedef struct _z_query_rc_t _z_query_rc_t; /** * The callback signature of the functions handling query messages. */ typedef void (*_z_closure_query_callback_t)(_z_query_rc_t *query, void *arg); typedef struct { _z_declared_keyexpr_t _key; uint32_t _id; _z_closure_query_callback_t _callback; _z_drop_handler_t _dropper; void *_arg; bool _complete; z_locality_t _allowed_origin; _z_sync_group_notifier_t _session_callback_drop_notifier; _z_sync_group_notifier_t _queryable_callback_drop_notifier; } _z_session_queryable_t; static inline _z_session_queryable_t _z_session_queryable_null(void) { _z_session_queryable_t qle = {0}; return qle; } bool _z_session_queryable_eq(const _z_session_queryable_t *one, const _z_session_queryable_t *two); void _z_session_queryable_clear(_z_session_queryable_t *res); _Z_REFCOUNT_DEFINE(_z_session_queryable, _z_session_queryable) _Z_ELEM_DEFINE(_z_session_queryable, _z_session_queryable_t, _z_noop_size, _z_session_queryable_clear, _z_noop_copy, _z_noop_move, _z_session_queryable_eq, _z_noop_cmp, _z_noop_hash) _Z_ELEM_DEFINE(_z_session_queryable_rc, _z_session_queryable_rc_t, _z_session_queryable_rc_size, _z_session_queryable_rc_drop, _z_session_queryable_rc_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_session_queryable_rc, _z_session_queryable_rc_t, true) // Forward declaration to avoid cyclical includes typedef struct _z_reply_t _z_reply_t; typedef _z_slist_t _z_pending_reply_slist_t; typedef struct _z_reply_t _z_reply_t; /** * The callback signature of the functions handling query replies. */ typedef void (*_z_closure_reply_callback_t)(_z_reply_t *reply, void *arg); typedef struct _z_pending_query_t _z_pending_query_t; #ifdef Z_FEATURE_UNSTABLE_API typedef struct { _z_cancellation_token_rc_t _cancellation_token; size_t _handler_id; _z_sync_group_notifier_t _notifier; } _z_pending_query_cancellation_data_t; static inline _z_pending_query_cancellation_data_t _z_pending_query_cancellation_data_null(void) { _z_pending_query_cancellation_data_t d = {0}; return d; } static inline void _z_pending_query_cancellation_data_clear(_z_pending_query_cancellation_data_t *data) { if (!_Z_RC_IS_NULL(&data->_cancellation_token)) { _z_cancellation_token_remove_on_cancel_handler(_Z_RC_IN_VAL(&data->_cancellation_token), data->_handler_id); _z_cancellation_token_rc_drop(&data->_cancellation_token); _z_sync_group_notifier_drop(&data->_notifier); } } z_result_t _z_pending_query_register_cancellation(_z_pending_query_t *pq, const _z_cancellation_token_rc_t *opt_cancellation_token, const _z_session_rc_t *session); void _z_pending_query_cancellation_data_clear(_z_pending_query_cancellation_data_t *data); #endif struct _z_pending_query_t { _z_keyexpr_t _key; _z_optional_id_t _querier_id; _z_zint_t _id; _z_closure_reply_callback_t _callback; _z_drop_handler_t _dropper; z_locality_t _allowed_destination; z_clock_t _start_time; uint64_t _timeout; void *_arg; uint32_t _remaining_finals; _z_pending_reply_slist_t *_pending_replies; z_query_target_t _target; z_consolidation_mode_t _consolidation; bool _anyke; #ifdef Z_FEATURE_UNSTABLE_API _z_pending_query_cancellation_data_t _cancellation_data; #endif }; bool _z_pending_query_eq(const _z_pending_query_t *one, const _z_pending_query_t *two); void _z_pending_query_clear(_z_pending_query_t *res); _Z_ELEM_DEFINE(_z_pending_query, _z_pending_query_t, _z_noop_size, _z_pending_query_clear, _z_noop_copy, _z_noop_move, _z_pending_query_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_pending_query, _z_pending_query_t, false) struct __z_hello_handler_wrapper_t; // Forward declaration to be used in _z_closure_hello_callback_t /** * The callback signature of the functions handling hello messages. */ typedef void (*_z_closure_hello_callback_t)(_z_hello_t *hello, struct __z_hello_handler_wrapper_t *arg); z_result_t _z_session_generate_zid(_z_id_t *bs, uint8_t size); typedef enum { _Z_INTEREST_MSG_TYPE_FINAL = 0, _Z_INTEREST_MSG_TYPE_DECL_SUBSCRIBER = 1, _Z_INTEREST_MSG_TYPE_DECL_QUERYABLE = 2, _Z_INTEREST_MSG_TYPE_DECL_TOKEN = 3, _Z_INTEREST_MSG_TYPE_UNDECL_SUBSCRIBER = 4, _Z_INTEREST_MSG_TYPE_UNDECL_QUERYABLE = 5, _Z_INTEREST_MSG_TYPE_UNDECL_TOKEN = 6, _Z_INTEREST_MSG_TYPE_CONNECTION_DROPPED = 7, } _z_interest_msg_type_t; typedef struct _z_interest_msg_t { uint32_t id; uint8_t type; const _z_keyexpr_t *key; bool is_complete; } _z_interest_msg_t; /** * The callback signature of the functions handling interest messages. */ typedef void (*_z_interest_handler_t)(const _z_interest_msg_t *msg, _z_transport_peer_common_t *peer, void *arg); typedef struct { _z_keyexpr_t _key; uint32_t _id; _z_interest_handler_t _callback; _z_void_rc_t _arg; uint8_t _flags; } _z_session_interest_t; bool _z_session_interest_eq(const _z_session_interest_t *one, const _z_session_interest_t *two); void _z_session_interest_clear(_z_session_interest_t *res); static inline bool _z_session_interest_is_aggregate(const _z_session_interest_t *intr) { return (intr->_flags & _Z_INTEREST_FLAG_AGGREGATE) != 0; } _Z_REFCOUNT_DEFINE(_z_session_interest, _z_session_interest) _Z_ELEM_DEFINE(_z_session_interest, _z_session_interest_t, _z_noop_size, _z_session_interest_clear, _z_noop_copy, _z_noop_move, _z_session_interest_eq, _z_noop_cmp, _z_noop_hash) _Z_ELEM_DEFINE(_z_session_interest_rc, _z_session_interest_rc_t, _z_session_interest_rc_size, _z_session_interest_rc_drop, _z_session_interest_rc_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_session_interest_rc, _z_session_interest_rc_t, true) typedef enum { _Z_DECLARE_TYPE_SUBSCRIBER = 0, _Z_DECLARE_TYPE_QUERYABLE = 1, _Z_DECLARE_TYPE_TOKEN = 2, } _z_declare_type_t; typedef struct { _z_keyexpr_t _key; _z_transport_peer_common_t *_peer; uint32_t _id; uint8_t _type; bool _complete; } _z_declare_data_t; void _z_declare_data_clear(_z_declare_data_t *data); size_t _z_declare_data_size(_z_declare_data_t *data); void _z_declare_data_copy(_z_declare_data_t *dst, const _z_declare_data_t *src); _Z_ELEM_DEFINE(_z_declare_data, _z_declare_data_t, _z_declare_data_size, _z_declare_data_clear, _z_declare_data_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_declare_data, _z_declare_data_t, true) #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_SESSION_H */ ================================================ FILE: include/zenoh-pico/session/subscription.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_SUBSCRIPTION_H #define INCLUDE_ZENOH_PICO_SESSION_SUBSCRIPTION_H #include "zenoh-pico/collections/lru_cache.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/session.h" #ifdef __cplusplus extern "C" { #endif // Forward declaration to avoid cyclical include typedef struct _z_session_t _z_session_t; _Z_SVEC_DEFINE(_z_subscription_rc, _z_subscription_rc_t) _Z_REFCOUNT_DEFINE(_z_subscription_rc_svec, _z_subscription_rc_svec) typedef struct { _z_keyexpr_t ke; _z_subscription_rc_svec_rc_t infos; bool is_remote; } _z_subscription_cache_data_t; static inline _z_subscription_cache_data_t _z_subscription_cache_data_null(void) { _z_subscription_cache_data_t ret = {0}; return ret; } void _z_unsafe_subscription_cache_invalidate(_z_session_t *zn); int _z_subscription_cache_data_compare(const void *first, const void *second); void _z_subscription_cache_data_clear(_z_subscription_cache_data_t *val); /*------------------ Subscription ------------------*/ z_result_t _z_trigger_liveliness_subscriptions_declare(_z_session_t *zn, const _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer); z_result_t _z_trigger_liveliness_subscriptions_undeclare(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_timestamp_t *timestamp); #if Z_FEATURE_SUBSCRIPTION == 1 #if Z_FEATURE_RX_CACHE == 1 _Z_ELEM_DEFINE(_z_subscription, _z_subscription_cache_data_t, _z_noop_size, _z_subscription_cache_data_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_LRU_CACHE_DEFINE(_z_subscription, _z_subscription_cache_data_t, _z_subscription_cache_data_compare) #endif _z_subscription_rc_t _z_get_subscription_by_id(_z_session_t *zn, _z_subscriber_kind_t kind, const _z_zint_t id); _z_subscription_rc_t _z_register_subscription(_z_session_t *zn, _z_subscriber_kind_t kind, _z_subscription_t *sub); z_result_t _z_trigger_subscriptions_impl(_z_session_t *zn, _z_subscriber_kind_t sub_kind, _z_wireexpr_t *wireexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const _z_zint_t sample_kind, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer); void _z_unregister_subscription(_z_session_t *zn, _z_subscriber_kind_t kind, _z_subscription_rc_t *sub); void _z_flush_subscriptions(_z_session_t *zn); static inline z_result_t _z_trigger_subscriptions_put(_z_session_t *zn, _z_wireexpr_t *wireexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer) { return _z_trigger_subscriptions_impl(zn, _Z_SUBSCRIBER_KIND_SUBSCRIBER, wireexpr, payload, encoding, Z_SAMPLE_KIND_PUT, timestamp, qos, attachment, reliability, source_info, peer); } static inline z_result_t _z_trigger_subscriptions_del(_z_session_t *zn, _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer) { _z_encoding_t encoding = _z_encoding_null(); _z_bytes_t payload = _z_bytes_null(); return _z_trigger_subscriptions_impl(zn, _Z_SUBSCRIBER_KIND_SUBSCRIBER, wireexpr, &payload, &encoding, Z_SAMPLE_KIND_DELETE, timestamp, qos, attachment, reliability, source_info, peer); } #else // Z_FEATURE_SUBSCRIPTION == 0 static inline z_result_t _z_trigger_subscriptions_put(_z_session_t *zn, _z_wireexpr_t *wireexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(wireexpr); _ZP_UNUSED(payload); _ZP_UNUSED(encoding); _ZP_UNUSED(qos); _ZP_UNUSED(timestamp); _ZP_UNUSED(attachment); _ZP_UNUSED(reliability); _ZP_UNUSED(source_info); _ZP_UNUSED(peer); return _Z_RES_OK; } static inline z_result_t _z_trigger_subscriptions_del(_z_session_t *zn, _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(wireexpr); _ZP_UNUSED(qos); _ZP_UNUSED(timestamp); _ZP_UNUSED(attachment); _ZP_UNUSED(reliability); _ZP_UNUSED(source_info); _ZP_UNUSED(peer); return _Z_RES_OK; } #endif // Z_FEATURE_SUBSCRIPTION == 1 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_SUBSCRIPTION_H */ ================================================ FILE: include/zenoh-pico/session/utils.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_UTILS_H #define INCLUDE_ZENOH_PICO_SESSION_UTILS_H #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif /*------------------ Session ------------------*/ _z_hello_slist_t *_z_scout_inner(const z_what_t what, _z_id_t id, _z_string_t *locator, const uint32_t timeout, const bool exit_on_first); z_result_t _z_session_init(_z_session_t *zn, const _z_id_t *zid); void _z_session_clear(_z_session_t *zn); z_result_t _z_session_close(_z_session_t *zn); z_result_t _z_handle_network_message(_z_transport_common_t *transport, _z_zenoh_message_t *z_msg, _z_transport_peer_common_t *peer); #if Z_FEATURE_MULTI_THREAD == 1 static inline z_result_t _z_session_mutex_lock(_z_session_t *zn) { return _z_mutex_lock(&zn->_mutex_inner); } static inline z_result_t _z_session_mutex_lock_if_open(_z_session_t *zn) { _Z_RETURN_IF_ERR(_z_mutex_lock(&zn->_mutex_inner)); if (_z_session_is_closed(zn)) { _z_mutex_unlock(&zn->_mutex_inner); return _Z_ERR_SESSION_CLOSED; } return _Z_RES_OK; } static inline void _z_session_mutex_unlock(_z_session_t *zn) { (void)_z_mutex_unlock(&zn->_mutex_inner); } static inline void _z_session_transport_mutex_lock(_z_session_t *zn) { (void)_z_mutex_rec_lock(&zn->_mutex_transport); } static inline void _z_session_transport_mutex_unlock(_z_session_t *zn) { (void)_z_mutex_rec_unlock(&zn->_mutex_transport); } #if Z_FEATURE_ADMIN_SPACE == 1 static inline void _z_session_admin_space_mutex_lock(_z_session_t *zn) { (void)_z_mutex_lock(&zn->_mutex_admin_space); } static inline void _z_session_admin_space_mutex_unlock(_z_session_t *zn) { (void)_z_mutex_unlock(&zn->_mutex_admin_space); } #else static inline void _z_session_admin_space_mutex_lock(_z_session_t *zn) { _ZP_UNUSED(zn); } static inline void _z_session_admin_space_mutex_unlock(_z_session_t *zn) { _ZP_UNUSED(zn); } #endif #else static inline z_result_t _z_session_mutex_lock(_z_session_t *zn) { _ZP_UNUSED(zn); return _Z_RES_OK; } static inline z_result_t _z_session_mutex_lock_if_open(_z_session_t *zn) { return _z_session_is_closed(zn) ? _Z_ERR_SESSION_CLOSED : _Z_RES_OK; } static inline void _z_session_mutex_unlock(_z_session_t *zn) { _ZP_UNUSED(zn); } static inline void _z_session_transport_mutex_lock(_z_session_t *zn) { _ZP_UNUSED(zn); } static inline void _z_session_transport_mutex_unlock(_z_session_t *zn) { _ZP_UNUSED(zn); } static inline void _z_session_admin_space_mutex_lock(_z_session_t *zn) { _ZP_UNUSED(zn); } static inline void _z_session_admin_space_mutex_unlock(_z_session_t *zn) { _ZP_UNUSED(zn); } #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_UTILS_H */ ================================================ FILE: include/zenoh-pico/session/weak_session.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_SESSION_WEAK_SESSION_H #define INCLUDE_ZENOH_PICO_SESSION_WEAK_SESSION_H #include "zenoh-pico/collections/refcount.h" #ifdef __cplusplus extern "C" { #endif // Forward declaration to avoid cyclical include typedef struct _z_session_t _z_session_t; extern void _z_session_clear(_z_session_t *zn); _Z_REFCOUNT_DEFINE_NO_FROM_VAL(_z_session, _z_session) #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_SESSION_WEAK_SESSION_H */ ================================================ FILE: include/zenoh-pico/system/common/platform.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #ifndef ZENOH_PICO_SYSTEM_PLATFORM_COMMON_H #define ZENOH_PICO_SYSTEM_PLATFORM_COMMON_H #ifndef SPHINX_DOCS // For some reason sphinx/clang doesn't handle bool types correctly if stdbool.h is included #include #endif #include #include "zenoh-pico/api/olv_macros.h" #include "zenoh-pico/config.h" #include "zenoh-pico/utils/result.h" /* Centralized built-in socket markers for TCP/UDP/WS/TLS transports that use * the platform socket layer. */ #if !defined(ZP_PLATFORM_SOCKET_POSIX) && (defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD)) #define ZP_PLATFORM_SOCKET_POSIX 1 #endif #if !defined(ZP_PLATFORM_SOCKET_WINDOWS) && defined(ZENOH_WINDOWS) #define ZP_PLATFORM_SOCKET_WINDOWS 1 #endif #if !defined(ZP_PLATFORM_SOCKET_ZEPHYR) && defined(ZENOH_ZEPHYR) #define ZP_PLATFORM_SOCKET_ZEPHYR 1 #endif #if !defined(ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP) && defined(ZENOH_FREERTOS_PLUS_TCP) #define ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP 1 #endif #if !defined(ZP_PLATFORM_SOCKET_LWIP) && (defined(ZENOH_FREERTOS_LWIP) || defined(ZENOH_RPI_PICO)) #define ZP_PLATFORM_SOCKET_LWIP 1 #endif #if !defined(ZP_PLATFORM_SOCKET_ESP32) && (defined(ZENOH_ESPIDF) || defined(ZENOH_ARDUINO_ESP32)) #define ZP_PLATFORM_SOCKET_ESP32 1 #endif #if !defined(ZP_PLATFORM_SOCKET_MBED) && defined(ZENOH_MBED) #define ZP_PLATFORM_SOCKET_MBED 1 #endif #if !defined(ZP_PLATFORM_SOCKET_OPENCR) && defined(ZENOH_ARDUINO_OPENCR) #define ZP_PLATFORM_SOCKET_OPENCR 1 #endif #if !defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) && \ (Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1 || Z_FEATURE_LINK_WS == 1 || \ Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1) #define ZP_PLATFORM_SOCKET_LINKS_ENABLED 1 #endif #ifdef ZP_SYSTEM_PLATFORM_HEADER #include ZP_SYSTEM_PLATFORM_HEADER #elif defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD) #include "zenoh-pico/system/platform/unix.h" #elif defined(ZENOH_WINDOWS) #define NOMINMAX #include "zenoh-pico/system/platform/windows.h" #elif defined(ZENOH_ESPIDF) #include "zenoh-pico/system/platform/espidf.h" #elif defined(ZENOH_MBED) #include "zenoh-pico/system/platform/mbed.h" #elif defined(ZENOH_ZEPHYR) #include "zenoh-pico/system/platform/zephyr.h" #elif defined(ZENOH_ARDUINO_ESP32) #include "zenoh-pico/system/platform/arduino/esp32.h" #elif defined(ZENOH_ARDUINO_OPENCR) #include "zenoh-pico/system/platform/arduino/opencr.h" #elif defined(ZENOH_EMSCRIPTEN) #include "zenoh-pico/system/platform/emscripten.h" #elif defined(ZENOH_FLIPPER) #include "zenoh-pico/system/platform/flipper.h" #elif defined(ZENOH_FREERTOS_PLUS_TCP) #include "zenoh-pico/system/platform/freertos/freertos_plus_tcp.h" #elif defined(ZENOH_FREERTOS_LWIP) #include "zenoh-pico/system/platform/freertos/lwip.h" #elif defined(ZENOH_RPI_PICO) #include "zenoh-pico/system/platform/rpi_pico.h" #elif defined(ZENOH_GENERIC) #include "zenoh_generic_platform.h" #elif defined(ZENOH_THREADX_STM32) #include "zenoh-pico/system/platform/threadx/stm32.h" #else #include "zenoh-pico/system/platform/void.h" #error "Unknown platform" #endif #ifdef __cplusplus extern "C" { #endif /*------------------ Random ------------------*/ /** * Generates a random unsigned 8-bit integer. */ uint8_t z_random_u8(void); /** * Generates a random unsigned 16-bit integer. */ uint16_t z_random_u16(void); /** * Generates a random unsigned 32-bit integer. */ uint32_t z_random_u32(void); /** * Generates a random unsigned 64-bit integer. */ uint64_t z_random_u64(void); /** * Fills buffer with random data. * * Parameters: * buf: Pointer to the buffer that will be filled with random data. * len: Number of bytes to fill in the buffer. */ void z_random_fill(void *buf, size_t len); /*------------------ Memory ------------------*/ /** * Allocates memory of the specified size. * * Parameters: * size: The number of bytes to allocate. * * Returns: * A pointer to the allocated memory, or NULL if the allocation fails. */ void *z_malloc(size_t size); /** * Reallocates the given memory block to a new size. * * Parameters: * ptr: Pointer to the previously allocated memory. Can be NULL, in which case it behaves like z_malloc(). * size: The new size for the memory block in bytes. * * Returns: * A pointer to the reallocated memory, or NULL if the reallocation fails. */ void *z_realloc(void *ptr, size_t size); /** * Frees the memory previously allocated by z_malloc or z_realloc. * * Parameters: * ptr: Pointer to the memory to be freed. If NULL, no action is taken. */ void z_free(void *ptr); #if Z_FEATURE_MULTI_THREAD == 0 // dummy types for correct macros work typedef void *_z_task_t; typedef void *_z_mutex_t; typedef void *_z_mutex_rec_t; typedef void *_z_condvar_t; typedef void *z_task_attr_t; typedef void *_z_task_id_t; #endif /*------------------ Thread ------------------*/ _Z_OWNED_TYPE_VALUE(_z_task_t, task) _Z_OWNED_FUNCTIONS_SYSTEM_DEF(task) z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg); z_result_t _z_task_join(_z_task_t *task); z_result_t _z_task_detach(_z_task_t *task); z_result_t _z_task_cancel(_z_task_t *task); void _z_task_exit(void); void _z_task_free(_z_task_t **task); _z_task_id_t _z_task_get_id(const _z_task_t *task); _z_task_id_t _z_task_current_id(void); bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r); /** * Constructs a new task. * * Parameters: * task: An uninitialized memory location where task will be constructed. * attr: Attributes of the task. * fun: Function to be executed by the task. * arg: Argument that will be passed to the function `fun`. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_task_init(z_owned_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg); /** * Joins the task and releases all allocated resources. * * Parameters: * task: Pointer to a :c:type:`z_moved_task_t` representing the task to be joined. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_task_join(z_moved_task_t *task); /** * Detaches the task and releases all allocated resources. * * Parameters: * task: Pointer to a :c:type:`z_moved_task_t` representing the task to be detached. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_task_detach(z_moved_task_t *task); /** * Drops the task. Same as :c:func:`z_task_detach`. Use :c:func:`z_task_join` to wait for the task completion. * * Parameters: * task: Pointer to a :c:type:`z_moved_task_t` representing the task to be dropped. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_task_drop(z_moved_task_t *task); /*------------------ Mutex ------------------*/ _Z_OWNED_TYPE_VALUE(_z_mutex_t, mutex) _Z_OWNED_FUNCTIONS_SYSTEM_DEF(mutex) z_result_t _z_mutex_init(_z_mutex_t *m); z_result_t _z_mutex_drop(_z_mutex_t *m); z_result_t _z_mutex_lock(_z_mutex_t *m); z_result_t _z_mutex_try_lock(_z_mutex_t *m); z_result_t _z_mutex_unlock(_z_mutex_t *m); z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m); z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m); z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m); z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m); z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m); /** * Constructs a mutex. * * Parameters: * m: Pointer to an uninitialized :c:type:`z_owned_mutex_t` that will be constructed. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_mutex_init(z_owned_mutex_t *m); /** * Drops a mutex and resets it to its gravestone state. * * Parameters: * m: Pointer to a :c:type:`z_moved_mutex_t` that will be dropped. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_mutex_drop(z_moved_mutex_t *m); /** * Locks a mutex. If the mutex is already locked, blocks the thread until it acquires the lock. * * Parameters: * m: Pointer to a :c:type:`z_loaned_mutex_t` that will be locked. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_mutex_lock(z_loaned_mutex_t *m); /** * Tries to lock a mutex. If the mutex is already locked, the function returns immediately. * * Parameters: * m: Pointer to a :c:type:`z_loaned_mutex_t` that will be locked if not already locked. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_mutex_try_lock(z_loaned_mutex_t *m); /** * Unlocks a previously locked mutex. If the mutex was not locked by the current thread, the behavior is undefined. * * Parameters: * m: Pointer to a :c:type:`z_loaned_mutex_t` that will be unlocked. * * Returns: * ``0`` in case of success, negative error code otherwise. */ z_result_t z_mutex_unlock(z_loaned_mutex_t *m); /*------------------ CondVar ------------------*/ _Z_OWNED_TYPE_VALUE(_z_condvar_t, condvar) _Z_OWNED_FUNCTIONS_SYSTEM_DEF(condvar) z_result_t _z_condvar_init(_z_condvar_t *cv); z_result_t _z_condvar_drop(_z_condvar_t *cv); z_result_t _z_condvar_signal(_z_condvar_t *cv); z_result_t _z_condvar_signal_all(_z_condvar_t *cv); z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m); z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime); /** * Initializes a condition variable. * * Parameters: * cv: Pointer to an uninitialized :c:type:`z_owned_condvar_t` that will be initialized. * * Returns: * ``0`` if the initialization is successful, a negative value otherwise. */ z_result_t z_condvar_init(z_owned_condvar_t *cv); /** * Destroys a condition variable and releases its resources. * * Parameters: * cv: Pointer to a :c:type:`z_moved_condvar_t` that will be destroyed. * * Returns: * ``0`` if the destruction is successful, a negative value otherwise. */ z_result_t z_condvar_drop(z_moved_condvar_t *cv); /** * Signals (wakes up) one thread waiting on the condition variable. * * Parameters: * cv: Pointer to a :c:type:`z_loaned_condvar_t` that will be signaled. * * Returns: * ``0`` if the signal is successful, a negative value otherwise. */ z_result_t z_condvar_signal(z_loaned_condvar_t *cv); /** * Waits for a signal on the condition variable while holding a mutex. * * The calling thread is blocked until the condition variable is signaled. * The associated mutex must be locked by the calling thread, and it will be automatically unlocked while waiting. * * Parameters: * cv: Pointer to a :c:type:`z_loaned_condvar_t` on which to wait. * m: Pointer to a :c:type:`z_loaned_mutex_t` that will be unlocked during the wait. * * Returns: * ``0`` if the wait is successful, a negative value otherwise. */ z_result_t z_condvar_wait(z_loaned_condvar_t *cv, z_loaned_mutex_t *m); /** * Waits for a signal on the condition variable while holding a mutex until a specified time. * * The calling thread is blocked until the condition variable is signaled or the timeout occurs. * The associated mutex must be locked by the calling thread, and it will be automatically unlocked while waiting. * * Parameters: * cv: Pointer to a :c:type:`z_loaned_condvar_t` on which to wait. * m: Pointer to a :c:type:`z_loaned_mutex_t` that will be unlocked during the wait. * abstime: Absolute end time. * * Returns: * ``0`` if the wait is successful, ``Z_ETIMEDOUT`` if a timeout occurred, other negative value otherwise. */ z_result_t z_condvar_wait_until(z_loaned_condvar_t *cv, z_loaned_mutex_t *m, const z_clock_t *abstime); /*------------------ Sleep ------------------*/ /** * Suspends execution for a specified amount of time in microseconds. * * Parameters: * time: The amount of time to sleep, in microseconds. * * Returns: * ``0`` if the sleep is successful, a negative value otherwise. */ z_result_t z_sleep_us(size_t time); /** * Suspends execution for a specified amount of time in milliseconds. * * Parameters: * time: The amount of time to sleep, in milliseconds. * * Returns: * ``0`` if the sleep is successful, a negative value otherwise. */ z_result_t z_sleep_ms(size_t time); /** * Suspends execution for a specified amount of time in seconds. * * Parameters: * time: The amount of time to sleep, in seconds. * * Returns: * ``0`` if the sleep is successful, a negative value otherwise. */ z_result_t z_sleep_s(size_t time); /*------------------ Clock ------------------*/ /** * Returns monotonic clock time point corresponding to the current time instant. */ z_clock_t z_clock_now(void); unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch); unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch); unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch); /** * Returns the elapsed time in microseconds since a given clock time. * * Parameters: * time: Pointer to a `z_clock_t` representing the starting time. * * Returns: * The elapsed time in microseconds. */ unsigned long z_clock_elapsed_us(z_clock_t *time); /** * Returns the elapsed time in milliseconds since a given clock time. * * Parameters: * time: Pointer to a `z_clock_t` representing the starting time. * * Returns: * The elapsed time in milliseconds. */ unsigned long z_clock_elapsed_ms(z_clock_t *time); /** * Returns the elapsed time in seconds since a given clock time. * * Parameters: * time: Pointer to a `z_clock_t` representing the starting time. * * Returns: * The elapsed time in seconds. */ unsigned long z_clock_elapsed_s(z_clock_t *time); /** * Offsets the clock by a specified duration in microseconds. * * Parameters: * clock: Pointer to a `z_clock_t` to offset. * duration: The duration in microseconds. */ void z_clock_advance_us(z_clock_t *clock, unsigned long duration); /** * Offsets the clock by a specified duration in milliseconds. * * Parameters: * clock: Pointer to a `z_clock_t` to offset. * duration: The duration in milliseconds. */ void z_clock_advance_ms(z_clock_t *clock, unsigned long duration); /** * Offsets the clock by a specified duration in seconds. * * Parameters: * clock: Pointer to a `z_clock_t` to offset. * duration: The duration in seconds. */ void z_clock_advance_s(z_clock_t *clock, unsigned long duration); /*------------------ Time ------------------*/ /** * Returns system clock time point corresponding to the current time instant. */ z_time_t z_time_now(void); /** * Gets the current time as a string. * * Parameters: * buf: Pointer to a buffer where the time string will be written. * buflen: The length of the buffer. * * Returns: * A pointer to the buffer containing the time string. */ const char *z_time_now_as_str(char *const buf, unsigned long buflen); /** * Returns the elapsed time in microseconds since a given time. * * Parameters: * time: Pointer to a `z_time_t` representing the starting time. * * Returns: * The elapsed time in microseconds. */ unsigned long z_time_elapsed_us(z_time_t *time); /** * Returns the elapsed time in milliseconds since a given time. * * Parameters: * time: Pointer to a `z_time_t` representing the starting time. * * Returns: * The elapsed time in milliseconds. */ unsigned long z_time_elapsed_ms(z_time_t *time); /** * Returns the elapsed time in seconds since a given time. * * Parameters: * time: Pointer to a `z_time_t` representing the starting time. * * Returns: * The elapsed time in seconds. */ unsigned long z_time_elapsed_s(z_time_t *time); typedef struct { uint32_t secs; uint32_t nanos; } _z_time_since_epoch; z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t); /*------------------ P2p unicast internal functions ------------------*/ z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking); z_result_t _z_ip_port_to_endpoint(const uint8_t *address, size_t address_len, uint16_t port, char *dst, size_t dst_len); z_result_t _z_socket_get_endpoints(const _z_sys_net_socket_t *sock, char *local, size_t local_len, char *remote, size_t remote_len); void _z_socket_close(_z_sys_net_socket_t *sock); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_PLATFORM_COMMON_H */ ================================================ FILE: include/zenoh-pico/system/common/system_error.h ================================================ #ifndef ZENOH_PICO_SYSTEM_COMMON_SYSTEM_ERROR_H #define ZENOH_PICO_SYSTEM_COMMON_SYSTEM_ERROR_H #include "zenoh-pico/utils/logging.h" #ifdef __cplusplus extern "C" { #endif static inline void _z_report_system_error(int errcode) { _Z_ERROR("System error: %i", errcode); } #define _Z_CHECK_SYS_ERR(expr) \ do { \ int __res = expr; \ if (__res != 0) { \ _z_report_system_error(__res); \ _Z_ERROR_RETURN(_Z_ERR_SYSTEM_GENERIC); \ } \ return _Z_RES_OK; \ } while (false) #define _Z_RETURN_IF_SYS_ERR(expr) \ do { \ int __res = expr; \ if (__res != 0) { \ _z_report_system_error(__res); \ _Z_ERROR_RETURN(_Z_ERR_SYSTEM_GENERIC); \ } \ } while (false) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_COMMON_SYSTEM_ERROR_H */ ================================================ FILE: include/zenoh-pico/system/platform/arduino/esp32.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_ESP32_TYPES_H #define ZENOH_PICO_SYSTEM_ESP32_TYPES_H #include #include #include #include "zenoh-pico/config.h" #if Z_FEATURE_MULTI_THREAD == 1 #include #endif // Z_FEATURE_MULTI_THREAD == 1 #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef struct { const char *name; UBaseType_t priority; size_t stack_depth; #if (configSUPPORT_STATIC_ALLOCATION == 1) bool static_allocation; StackType_t *stack_buffer; StaticTask_t *task_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } z_task_attr_t; typedef struct { TaskHandle_t handle; EventGroupHandle_t join_event; } _z_task_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef TaskHandle_t _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct BluetoothSerial BluetoothSerial; // Forward declaration to be used in _z_sys_net_socket_t typedef struct HardwareSerial HardwareSerial; // Forward declaration to be used in _z_sys_net_socket_t typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) int _fd; #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 BluetoothSerial *_bts; // As pointer to cross the boundary between C and C++ #endif #if Z_FEATURE_LINK_SERIAL == 1 HardwareSerial *_serial; // As pointer to cross the boundary between C and C++ #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on ESP32 port of Zenoh-Pico" #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_ESP32_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/arduino/opencr.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_ARDUINO_OPENCR_TYPES_H #define ZENOH_PICO_SYSTEM_ARDUINO_OPENCR_TYPES_H #include #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef void *_z_task_t; typedef void *z_task_attr_t; typedef void *_z_mutex_t; typedef void *_z_condvar_t; typedef void *_z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct IPAddress IPAddress; // Forward declaration to be used in __z_net_iptcp_addr_t typedef struct WiFiClient WiFiClient; // Forward declaration to be used in _z_sys_net_socket_t typedef struct WiFiUDP WiFiUDP; // Forward declaration to be used in _z_sys_net_socket_t typedef struct { union { #if Z_FEATURE_LINK_TCP == 1 WiFiClient *_tcp; // As pointer to cross the boundary between C and C++ #endif #if Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 WiFiUDP *_udp; // As pointer to cross the boundary between C and C++ #endif bool _err; }; } _z_sys_net_socket_t; typedef struct { IPAddress *_addr; // As pointer to cross the boundary between C and C++ uint16_t _port; } __z_net_iptcp_addr_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) __z_net_iptcp_addr_t _iptcp; #endif }; bool _err; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_ARDUINO_OPENCR_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/emscripten.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_WASM_TYPES_H #define ZENOH_PICO_SYSTEM_WASM_TYPES_H #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 #include typedef pthread_t _z_task_t; typedef pthread_attr_t z_task_attr_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef pthread_t _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef double z_clock_t; typedef double z_time_t; typedef struct { union { #if Z_FEATURE_LINK_WS == 1 struct { int _fd; uint32_t _tout; } _ws; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if Z_FEATURE_LINK_WS == 1 struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_WASM_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/espidf.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_ESPIDF_TYPES_H #define ZENOH_PICO_SYSTEM_ESPIDF_TYPES_H #include #include #include #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 #include typedef struct { const char *name; UBaseType_t priority; size_t stack_depth; #if (configSUPPORT_STATIC_ALLOCATION == 1) bool static_allocation; StackType_t *stack_buffer; StaticTask_t *task_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } z_task_attr_t; typedef struct { TaskHandle_t handle; EventGroupHandle_t join_event; } _z_task_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef TaskHandle_t _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) int _fd; #endif #if Z_FEATURE_LINK_SERIAL == 1 uart_port_t _serial; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on ESP-IDF port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on ESP-IDF port of Zenoh-Pico" #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_ESPIDF_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/flipper.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_FLIPPER_TYPES_H #define ZENOH_PICO_SYSTEM_FLIPPER_TYPES_H #include #include #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #define FLIPPER_DEFAULT_THREAD_STACK_SIZE 2048 #define FLIPPER_SERIAL_STREAM_BUFFER_SIZE 512 #define FLIPPER_SERIAL_STREAM_TRIGGERED_LEVEL 10 #define FLIPPER_SERIAL_TIMEOUT_MS 200 #if Z_FEATURE_MULTI_THREAD == 1 typedef FuriThread* _z_task_t; typedef uint32_t z_task_attr_t; typedef FuriMutex* _z_mutex_t; typedef void* _z_mutex_t; typedef void* _z_condvar_t; typedef FuriThreadId _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct { #if Z_FEATURE_LINK_SERIAL == 1 FuriStreamBuffer* _rx_stream; FuriHalSerialHandle* _serial; #endif } _z_sys_net_socket_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_FLIPPER_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/freertos/freertos_plus_tcp.h ================================================ // // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // Błażej Sowa, #ifndef ZENOH_PICO_SYSTEM_FREERTOS_PLUS_TCP_TYPES_H #define ZENOH_PICO_SYSTEM_FREERTOS_PLUS_TCP_TYPES_H #include #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "semphr.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef struct { const char *name; UBaseType_t priority; size_t stack_depth; #if (configSUPPORT_STATIC_ALLOCATION == 1) bool static_allocation; StackType_t *stack_buffer; StaticTask_t *task_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } z_task_attr_t; typedef struct { TaskHandle_t handle; EventGroupHandle_t join_event; void *(*fun)(void *); void *arg; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticEventGroup_t join_event_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_task_t; typedef struct { SemaphoreHandle_t handle; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticSemaphore_t buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_mutex_t; typedef _z_mutex_t _z_mutex_rec_t; typedef struct { SemaphoreHandle_t mutex; SemaphoreHandle_t sem; int waiters; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticSemaphore_t mutex_buffer; StaticSemaphore_t sem_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_condvar_t; typedef TaskHandle_t _z_task_id_t; #endif // Z_MULTI_THREAD == 1 typedef TickType_t z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) Socket_t _socket; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct freertos_addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif ================================================ FILE: include/zenoh-pico/system/platform/freertos/lwip.h ================================================ // // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // João Mário Lago, // #ifndef ZENOH_PICO_SYSTEM_FREERTOS_PLUS_LWIP_TYPES_H #define ZENOH_PICO_SYSTEM_FREERTOS_PLUS_LWIP_TYPES_H #include #include "FreeRTOS.h" #include "lwip/sockets.h" #include "semphr.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 #include "event_groups.h" typedef struct { const char *name; UBaseType_t priority; size_t stack_depth; #if (configSUPPORT_STATIC_ALLOCATION == 1) bool static_allocation; StackType_t *stack_buffer; StaticTask_t *task_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } z_task_attr_t; typedef struct { TaskHandle_t handle; EventGroupHandle_t join_event; void *(*fun)(void *); void *arg; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticEventGroup_t join_event_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_task_t; typedef struct { SemaphoreHandle_t handle; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticSemaphore_t buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_mutex_t; typedef _z_mutex_t _z_mutex_rec_t; typedef struct { SemaphoreHandle_t mutex; SemaphoreHandle_t sem; int waiters; #if (configSUPPORT_STATIC_ALLOCATION == 1) StaticSemaphore_t mutex_buffer; StaticSemaphore_t sem_buffer; #endif /* SUPPORT_STATIC_ALLOCATION */ } _z_condvar_t; typedef TaskHandle_t _z_task_id_t; #endif // Z_MULTI_THREAD == 1 typedef TickType_t z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) int _socket; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #if defined(ZP_PLATFORM_SOCKET_LWIP) && defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) #define ZP_LWIP_SOCKET_HELPERS_DEFINED 1 static inline int _z_lwip_socket_get(_z_sys_net_socket_t sock) { return sock._socket; } static inline void _z_lwip_socket_set(_z_sys_net_socket_t *sock, int fd) { sock->_socket = fd; } #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on FreeRTOS + LWIP port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_SERIAL == 1 #error "Serial not supported yet on FreeRTOS + LWIP port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on FreeRTOS + LWIP port of Zenoh-Pico" #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: include/zenoh-pico/system/platform/mbed.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_MBED_TYPES_H #define ZENOH_PICO_SYSTEM_MBED_TYPES_H #include #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif typedef int _z_socket_t; #if Z_FEATURE_MULTI_THREAD == 1 typedef void *_z_task_t; // Workaround as MBED is a C++ library typedef void *z_task_attr_t; // Workaround as MBED is a C++ library typedef void *_z_mutex_t; // Workaround as MBED is a C++ library typedef void *_z_mutex_rec_t; // Workaround as MBED is a C++ library typedef void *_z_condvar_t; // Workaround as MBED is a C++ library typedef void *_z_task_id_t; // osThreadId_t is defined as void* in MBED #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct BufferedSerial BufferedSerial; // Forward declaration to be used in _z_sys_net_socket_t typedef struct UDPSocket UDPSocket; // Forward declaration to be used in _z_sys_net_socket_t typedef struct TCPSocket TCPSocket; // Forward declaration to be used in _z_sys_net_socket_t typedef struct SocketAddress SocketAddress; // Forward declaration to be used in _z_sys_net_endpoint_t typedef struct { bool _err; union { #if Z_FEATURE_LINK_TCP == 1 TCPSocket *_tcp; // As pointer to cross the boundary between C and C++ #endif #if Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 UDPSocket *_udp; // As pointer to cross the boundary between C and C++ #endif #if Z_FEATURE_LINK_SERIAL == 1 BufferedSerial *_serial; // As pointer to cross the boundary between C and C++ #endif }; } _z_sys_net_socket_t; typedef struct { bool _err; union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) SocketAddress *_iptcp; // As pointer to cross the boundary between C and C++ #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_MBED_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/rpi_pico.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_RPI_PICO_TYPES_H #define ZENOH_PICO_SYSTEM_RPI_PICO_TYPES_H #include #include "FreeRTOS.h" #include "event_groups.h" #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 #include "lwip/ip4_addr.h" #endif #if Z_FEATURE_LINK_SERIAL == 1 #include "hardware/gpio.h" #include "hardware/uart.h" #endif #include "semphr.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef struct { const char *name; UBaseType_t priority; size_t stack_depth; } z_task_attr_t; typedef struct { TaskHandle_t handle; EventGroupHandle_t join_event; } _z_task_t; typedef SemaphoreHandle_t _z_mutex_t; typedef SemaphoreHandle_t _z_mutex_rec_t; typedef struct { SemaphoreHandle_t mutex; SemaphoreHandle_t sem; int waiters; } _z_condvar_t; typedef TaskHandle_t _z_task_id_t; #endif // Z_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) int _fd; #endif #if Z_FEATURE_LINK_SERIAL == 1 uart_inst_t *_serial; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #if defined(ZP_PLATFORM_SOCKET_LWIP) && defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) #define ZP_LWIP_SOCKET_HELPERS_DEFINED 1 static inline int _z_lwip_socket_get(_z_sys_net_socket_t sock) { return sock._fd; } static inline void _z_lwip_socket_set(_z_sys_net_socket_t *sock, int fd) { sock->_fd = fd; } #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on Raspberry Pi Pico W port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on Raspberry Pi Pico W port of Zenoh-Pico" #endif #ifdef __cplusplus } #endif #if Z_FEATURE_LINK_SERIAL_USB == 1 void _z_usb_uart_init(); void _z_usb_uart_deinit(); void _z_usb_uart_write(const uint8_t *buf, int length); uint8_t _z_usb_uart_getc(); #endif #endif // ZENOH_PICO_SYSTEM_RPI_PICO_TYPES_H ================================================ FILE: include/zenoh-pico/system/platform/threadx/stm32.h ================================================ #ifndef ZENOH_PICO_SYSTEM_THREADX_TYPES_H #define ZENOH_PICO_SYSTEM_THREADX_TYPES_H #include #include "zenoh-pico/config.h" /* * hal.h needs to be provided by application layer. It should include * the necessary stm32 HAL headers for your target (ex. stm32f4xx_hal.h). */ #include "hal.h" #if Z_FEATURE_MULTI_THREAD == 1 #include "tx_api.h" #endif // Z_FEATURE_MULTI_THREAD == 1 #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 #ifndef Z_TASK_STACK_SIZE #define Z_TASK_STACK_SIZE 4096 #endif #ifndef Z_TASK_PRIORITY #define Z_TASK_PRIORITY 14 #endif #ifndef Z_TASK_PREEMPT_THRESHOLD #define Z_TASK_PREEMPT_THRESHOLD 14 #endif #ifndef Z_TASK_TIME_SLICE #define Z_TASK_TIME_SLICE 1 #endif /* * Use this define if you want to use functions HAL_UARTEx_RxEventCallback and * HAL_UART_ErrorCallback in your application with multiple other uart ports. * Application is then responisble for calling zptxstm32_rx_event_cb and zptxstm32_error_event_cb functions * inside these two HAL interrupts. */ #ifndef ZENOH_THREADX_STM32_GEN_IRQ #define ZENOH_THREADX_STM32_GEN_IRQ 1 #endif typedef struct { TX_THREAD threadx_thread; uint8_t threadx_stack[Z_TASK_STACK_SIZE]; } _z_task_t; typedef void *z_task_attr_t; // Not used typedef TX_MUTEX _z_mutex_rec_t; typedef TX_MUTEX _z_mutex_t; typedef struct { TX_MUTEX mutex; TX_SEMAPHORE sem; UINT waiters; } _z_condvar_t; typedef TX_THREAD *_z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef ULONG z_time_t; #if Z_FEATURE_LINK_SERIAL == 1 #ifndef ZENOH_HUART #error Please define ZENOH_HUART: STM32CubeMX - Connectivity – USARTx – User constants, click “Add IP Handle Label” and enter #error ZENOH_HUART for a serial port which will be used with zenoh-pico #error Alternatively, define ZENOH_HUART by hand, set to huart1, huart2, etc. #endif #endif typedef struct { union { void *_socket; }; } _z_sys_net_socket_t; typedef struct { union {}; } _z_sys_net_endpoint_t; #if ZENOH_THREADX_STM32_GEN_IRQ == 0 void zptxstm32_rx_event_cb(UART_HandleTypeDef *huart, uint16_t offset); void zptxstm32_error_event_cb(UART_HandleTypeDef *huart); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_THREADX_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/unix.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_UNIX_TYPES_H #define ZENOH_PICO_SYSTEM_UNIX_TYPES_H #include #include #include #include #if Z_FEATURE_MULTI_THREAD == 1 #include #endif // Z_FEATURE_MULTI_THREAD == 1 #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef pthread_t _z_task_t; typedef pthread_attr_t z_task_attr_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef pthread_t _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; typedef struct { union { #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 || \ Z_FEATURE_RAWETH_TRANSPORT == 1 || Z_FEATURE_LINK_SERIAL == 1 int _fd; #endif }; #if Z_FEATURE_LINK_TLS == 1 void *_tls_sock; // Pointer to _z_tls_socket_t #endif } _z_sys_net_socket_t; typedef struct { union { #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 || Z_FEATURE_LINK_UDP_UNICAST == 1 struct addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_UNIX_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform/void.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_VOID_H #define ZENOH_PICO_SYSTEM_VOID_H #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef void *_z_task_t; typedef void *z_task_attr_t; typedef void *_z_mutex_t; typedef void *_z_mutex_rec_t; typedef void *_z_condvar_t; typedef void *_z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef void *z_clock_t; typedef void *z_time_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_VOID_H */ ================================================ FILE: include/zenoh-pico/system/platform/windows.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_WINDOWS_H #define ZENOH_PICO_SYSTEM_WINDOWS_H #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef HANDLE *_z_task_t; typedef void *z_task_attr_t; // Not used in Windows typedef SRWLOCK _z_mutex_t; typedef CRITICAL_SECTION _z_mutex_rec_t; typedef CONDITION_VARIABLE _z_condvar_t; typedef DWORD _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef LARGE_INTEGER z_clock_t; typedef struct timeb z_time_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) SOCKET _fd; #endif } _sock; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct addrinfo *_iptcp; #endif } _ep; } _z_sys_net_endpoint_t; inline void __asm__(char *instruction) { (void)(instruction); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_VOID_H */ ================================================ FILE: include/zenoh-pico/system/platform/zephyr.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_SYSTEM_ZEPHYR_TYPES_H #define ZENOH_PICO_SYSTEM_ZEPHYR_TYPES_H #include #if KERNEL_VERSION_MAJOR == 2 #include #elif KERNEL_VERSION_MAJOR == 3 || KERNEL_VERSION_MAJOR == 4 #include #else #pragma GCC warning "This Zephyr version might not be supported." #include #endif #include #include "zenoh-pico/config.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 typedef pthread_t _z_task_t; typedef pthread_attr_t z_task_attr_t; typedef pthread_mutex_t _z_mutex_t; typedef pthread_mutex_t _z_mutex_rec_t; typedef pthread_cond_t _z_condvar_t; typedef pthread_t _z_task_id_t; #endif // Z_FEATURE_MULTI_THREAD == 1 typedef struct timespec z_clock_t; typedef struct timeval z_time_t; #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct zsock_addrinfo; #endif typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) int _fd; #endif #if Z_FEATURE_LINK_SERIAL == 1 const struct device *_serial; #endif }; } _z_sys_net_socket_t; typedef struct { union { #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) struct zsock_addrinfo *_iptcp; #endif }; } _z_sys_net_endpoint_t; #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_SYSTEM_ZEPHYR_TYPES_H */ ================================================ FILE: include/zenoh-pico/system/platform.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #ifndef ZENOH_PICO_SYSTEM_PLATFORM_H #define ZENOH_PICO_SYSTEM_PLATFORM_H #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/platform.h" #endif /* ZENOH_PICO_SYSTEM_PLATFORM_H */ ================================================ FILE: include/zenoh-pico/transport/common/lease.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_TRANSPORT_LEASE_H #define ZENOH_PICO_TRANSPORT_LEASE_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_send_join(_z_transport_t *zt); z_result_t _z_send_keep_alive(_z_transport_t *zt); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_LEASE_H */ ================================================ FILE: include/zenoh-pico/transport/common/read.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_TRANSPORT_READ_H #define ZENOH_PICO_TRANSPORT_READ_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_read(_z_transport_t *zt, bool single_read); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_READ_H */ ================================================ FILE: include/zenoh-pico/transport/common/rx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_TRANSPORT_RX_H #define ZENOH_PICO_TRANSPORT_RX_H #include "zenoh-pico/link/link.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif /*------------------ Transmission and Reception helpers ------------------*/ size_t _z_read_stream_size(_z_zbuf_t *zbuf); // Socket is assumed to be in blocking mode with a finite timeout. z_result_t _z_link_recv_t_msg(_z_transport_message_t *t_msg, const _z_link_t *zl, _z_sys_net_socket_t *socket, z_clock_t recv_deadline); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_RX_H */ ================================================ FILE: include/zenoh-pico/transport/common/transport.h ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_COMMON_TRANSPORT_H #define ZENOH_PICO_COMMON_TRANSPORT_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif void _z_transport_common_clear(_z_transport_common_t *ztc); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_COMMON_TRANSPORT_H*/ ================================================ FILE: include/zenoh-pico/transport/common/tx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_TRANSPORT_TX_H #define ZENOH_PICO_TRANSPORT_TX_H #include "zenoh-pico/link/link.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif void __unsafe_z_prepare_wbuf(_z_wbuf_t *buf, uint8_t link_flow_capability); void __unsafe_z_finalize_wbuf(_z_wbuf_t *buf, uint8_t link_flow_capability); /*This function is unsafe because it operates in potentially concurrent data.*Make sure that the following mutexes are locked before calling this function : *-ztu->mutex_tx */ z_result_t __unsafe_z_serialize_zenoh_fragment(_z_wbuf_t *dst, _z_wbuf_t *src, z_reliability_t reliability, size_t sn, bool first); /*------------------ Transmission and Reception helpers ------------------*/ z_result_t _z_transport_tx_send_t_msg(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg, _z_transport_peer_unicast_slist_t *peers); z_result_t _z_transport_tx_send_t_msg_wrapper(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg); z_result_t _z_send_t_msg(_z_transport_t *zt, const _z_transport_message_t *t_msg); z_result_t _z_link_send_t_msg(const _z_link_t *zl, const _z_transport_message_t *t_msg, _z_sys_net_socket_t *socket); z_result_t _z_send_n_msg(_z_session_t *zn, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl, void *peer); z_result_t _z_send_n_batch(_z_session_t *zn, z_congestion_control_t cong_ctrl); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_TX_H */ ================================================ FILE: include/zenoh-pico/transport/manager.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_TRANSPORT_MANAGER_H #define INCLUDE_ZENOH_PICO_TRANSPORT_MANAGER_H #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif enum _z_peer_op_e { _Z_PEER_OP_OPEN = 0, _Z_PEER_OP_LISTEN = 1, }; z_result_t _z_new_transport(_z_transport_t *zt, const _z_id_t *bs, const _z_string_t *locator, z_whatami_t mode, int peer_op, const _z_config_t *session_cfg, _z_runtime_t *runtime); z_result_t _z_new_peer(_z_transport_t *zt, const _z_id_t *session_id, const _z_string_t *locator, const _z_config_t *session_cfg); bool _z_transport_open_error_is_retryable(z_result_t ret); void _z_free_transport(_z_transport_t **zt); #if Z_FEATURE_UNICAST_PEER == 1 z_result_t _z_add_peers(_z_transport_t *zt, const _z_id_t *session_id, _z_pending_peers_t *pending_peers, const _z_config_t *session_cfg, bool exit_on_failure); _z_fut_fn_result_t _zp_add_peers_task_fn(void *ztu_arg, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_TRANSPORT_MANAGER_H */ ================================================ FILE: include/zenoh-pico/transport/multicast/lease.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_MULTICAST_LEASE_H #define ZENOH_PICO_MULTICAST_LEASE_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 z_result_t _zp_multicast_send_join(_z_transport_multicast_t *ztm); z_result_t _zp_multicast_send_keep_alive(_z_transport_multicast_t *ztm); _z_fut_fn_result_t _zp_multicast_keep_alive_task_fn(void *ztm_arg, _z_executor_t *executor); _z_fut_fn_result_t _zp_multicast_lease_task_fn(void *ztm_arg, _z_executor_t *executor); _z_fut_fn_result_t _zp_multicast_send_join_task_fn(void *ztm_arg, _z_executor_t *executor); _z_fut_fn_result_t _zp_multicast_failed_result(_z_transport_multicast_t *ztm, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_MULTICAST_LEASE_H */ ================================================ FILE: include/zenoh-pico/transport/multicast/read.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_MULTICAST_READ_H #define ZENOH_PICO_MULTICAST_READ_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _zp_multicast_read(_z_transport_multicast_t *ztm, bool single_read); _z_fut_fn_result_t _zp_multicast_read_task_fn(void *ztm_arg, _z_executor_t *executor); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_MULTICAST_READ_H */ ================================================ FILE: include/zenoh-pico/transport/multicast/rx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_MULTICAST_RX_H #define ZENOH_PICO_MULTICAST_RX_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_multicast_recv_zbuf(_z_transport_multicast_t *ztm, size_t *to_read); z_result_t _z_multicast_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg); z_result_t _z_multicast_handle_transport_message(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr); z_result_t _z_multicast_update_rx_buffer(_z_transport_multicast_t *ztm); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_LINK_RX_H */ ================================================ FILE: include/zenoh-pico/transport/multicast/transport.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_MULTICAST_TRANSPORT_H #define ZENOH_PICO_MULTICAST_TRANSPORT_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_multicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_multicast_establish_param_t *param); z_result_t _z_multicast_open_peer(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid); z_result_t _z_multicast_open_client(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid); z_result_t _z_multicast_send_close(_z_transport_multicast_t *ztm, uint8_t reason, bool link_only); z_result_t _z_multicast_transport_close(_z_transport_multicast_t *ztm, uint8_t reason); void _z_multicast_transport_clear(_z_transport_multicast_t *ztm); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_MULTICAST_TRANSPORT_H */ ================================================ FILE: include/zenoh-pico/transport/multicast.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_MULTICAST_H #define ZENOH_PICO_MULTICAST_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif void _zp_multicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback); void _zp_multicast_info_session(const _z_transport_t *zt, _z_config_t *ps); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_MULTICAST_H */ ================================================ FILE: include/zenoh-pico/transport/raweth/read.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_RAWETH_READ_H #define ZENOH_PICO_RAWETH_READ_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 z_result_t _zp_raweth_read(_z_transport_multicast_t *ztm, bool single_read); _z_fut_fn_result_t _zp_raweth_read_task_fn(void *ztm_arg, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_RAWETH_READ_H */ ================================================ FILE: include/zenoh-pico/transport/raweth/rx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_RAWETH_RX_H #define ZENOH_PICO_RAWETH_RX_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_raweth_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr); z_result_t _z_raweth_recv_t_msg_na(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr); z_result_t _z_raweth_update_rx_buff(_z_transport_multicast_t *ztm); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_RAWETH_RX_H */ ================================================ FILE: include/zenoh-pico/transport/raweth/tx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_RAWETH_TX_H #define ZENOH_PICO_RAWETH_TX_H #include "zenoh-pico/net/session.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_raweth_link_send_t_msg(const _z_link_t *zl, const _z_transport_message_t *t_msg); z_result_t _z_raweth_send_n_msg(_z_session_t *zn, const _z_network_message_t *z_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl); z_result_t _z_raweth_send_t_msg(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_RAWETH_TX_H */ ================================================ FILE: include/zenoh-pico/transport/transport.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_TRANSPORT_TRANSPORT_H #define INCLUDE_ZENOH_PICO_TRANSPORT_TRANSPORT_H #include #include #include "zenoh-pico/collections/element.h" #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/link.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/weak_session.h" #ifdef __cplusplus extern "C" { #endif enum _z_dbuf_state_e { _Z_DBUF_STATE_NULL = 0, _Z_DBUF_STATE_INIT = 1, _Z_DBUF_STATE_OVERFLOW = 2, }; enum _z_batching_state_e { _Z_BATCHING_IDLE = 0, _Z_BATCHING_ACTIVE = 1, }; // Forward declaration to avoid cyclical include typedef _z_slist_t _z_resource_slist_t; typedef struct { _z_id_t _remote_zid; z_whatami_t _remote_whatami; volatile bool _received; _z_resource_slist_t *_remote_resources; #if Z_FEATURE_CONNECTIVITY == 1 _z_string_t _link_src; _z_string_t _link_dst; #endif #if Z_FEATURE_FRAGMENTATION == 1 // Defragmentation buffers uint8_t _state_reliable; uint8_t _state_best_effort; _z_wbuf_t _dbuf_reliable; _z_wbuf_t _dbuf_best_effort; // Patch uint8_t _patch; #endif } _z_transport_peer_common_t; #if Z_FEATURE_CONNECTIVITY == 1 typedef struct { _z_id_t _remote_zid; z_whatami_t _remote_whatami; _z_string_t _link_src; _z_string_t _link_dst; bool _owns_endpoints; } _z_connectivity_peer_event_data_t; #endif void _z_transport_peer_common_clear(_z_transport_peer_common_t *src); void _z_transport_peer_common_copy(_z_transport_peer_common_t *dst, const _z_transport_peer_common_t *src); bool _z_transport_peer_common_eq(const _z_transport_peer_common_t *left, const _z_transport_peer_common_t *right); #if Z_FEATURE_CONNECTIVITY == 1 void _z_connectivity_peer_event_data_clear(_z_connectivity_peer_event_data_t *event_data); void _z_connectivity_peer_event_data_copy_from_common(_z_connectivity_peer_event_data_t *dst, const _z_transport_peer_common_t *src); void _z_connectivity_peer_event_data_alias_from_common(_z_connectivity_peer_event_data_t *dst, const _z_transport_peer_common_t *src); #endif typedef struct { _z_transport_peer_common_t common; _z_slice_t _remote_addr; _z_conduit_sn_list_t _sn_rx_sns; // SN numbers _z_zint_t _sn_res; volatile _z_zint_t _lease; } _z_transport_peer_multicast_t; size_t _z_transport_peer_multicast_size(const _z_transport_peer_multicast_t *src); void _z_transport_peer_multicast_clear(_z_transport_peer_multicast_t *src); void _z_transport_peer_multicast_copy(_z_transport_peer_multicast_t *dst, const _z_transport_peer_multicast_t *src); bool _z_transport_peer_multicast_eq(const _z_transport_peer_multicast_t *left, const _z_transport_peer_multicast_t *right); _Z_ELEM_DEFINE(_z_transport_peer_multicast, _z_transport_peer_multicast_t, _z_transport_peer_multicast_size, _z_transport_peer_multicast_clear, _z_transport_peer_multicast_copy, _z_noop_move, _z_transport_peer_multicast_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_transport_peer_multicast, _z_transport_peer_multicast_t, true) typedef enum _z_unicast_peer_flow_state_e { _Z_FLOW_STATE_INACTIVE = 0, _Z_FLOW_STATE_PENDING_SIZE = 1, _Z_FLOW_STATE_PENDING_DATA = 2, _Z_FLOW_STATE_READY = 3, } _z_unicast_peer_flow_state_e; typedef struct { _z_transport_peer_common_t common; _z_sys_net_socket_t _socket; // FIXME: Temporary ownership flag to avoid double-closing sockets // when link and peer structs alias the same underlying fd/TLS. // This should be replaced by proper, explicit ownership semantics // (e.g. a ref-counted socket/TLS handle or single authoritative owner). bool _owns_socket; // SN numbers _z_zint_t _sn_rx_reliable; _z_zint_t _sn_rx_best_effort; bool _pending; uint8_t flow_state; uint16_t flow_curr_size; _z_zbuf_t flow_buff; } _z_transport_peer_unicast_t; void _z_transport_peer_unicast_clear(_z_transport_peer_unicast_t *src); void _z_transport_peer_unicast_copy(_z_transport_peer_unicast_t *dst, const _z_transport_peer_unicast_t *src); size_t _z_transport_peer_unicast_size(const _z_transport_peer_unicast_t *src); bool _z_transport_peer_unicast_eq(const _z_transport_peer_unicast_t *left, const _z_transport_peer_unicast_t *right); _Z_ELEM_DEFINE(_z_transport_peer_unicast, _z_transport_peer_unicast_t, _z_transport_peer_unicast_size, _z_transport_peer_unicast_clear, _z_transport_peer_unicast_copy, _z_noop_move, _z_transport_peer_unicast_eq, _z_noop_cmp, _z_noop_hash) _Z_SLIST_DEFINE(_z_transport_peer_unicast, _z_transport_peer_unicast_t, true) #define _Z_RES_POOL_INIT_SIZE 8 // Arbitrary small value typedef enum _z_transport_state_t { _Z_TRANSPORT_STATE_CLOSED = 0, _Z_TRANSPORT_STATE_RECONNECTING = 1, _Z_TRANSPORT_STATE_OPEN = 2, } _z_transport_state_t; // Handles to the transport tasks, stored at predefined positions. // Used by the reconnect task to resume them after a successful reconnection. // Index via _Z_TRANSPORT_TASK_* constants defined below. #define _Z_TRANSPORT_TASK_KEEP_ALIVE 0 #define _Z_TRANSPORT_TASK_LEASE 1 #define _Z_TRANSPORT_TASK_READ 2 #define _Z_TRANSPORT_TASK_SEND_JOIN 3 // multicast / raweth only #define _Z_TRANSPORT_TASK_ADD_PEERS 4 // unicast only #define _Z_TRANSPORT_TASK_COUNT 5 #if Z_FEATURE_AUTO_RECONNECT == 1 typedef struct _z_transport_tasks_t { _z_fut_handle_t _task_handles[_Z_TRANSPORT_TASK_COUNT]; } _z_transport_tasks_t; #endif typedef struct { _z_session_weak_t _session; _z_link_t *_link; // TX and RX buffers _z_wbuf_t _wbuf; _z_zbuf_t _zbuf; // SN numbers _z_zint_t _sn_res; _z_zint_t _sn_tx_reliable; _z_zint_t _sn_tx_best_effort; volatile _z_zint_t _lease; volatile bool _transmitted; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_t _mutex_tx; _z_mutex_rec_t _mutex_peer; #endif // Transport batching #if Z_FEATURE_BATCHING == 1 uint8_t _batch_state; size_t _batch_count; #endif // Here we assume the value is set only by the session _z_open // and after it only read by the transport tasks, so we don't need to make it atomic or protect it with mutexes. _z_transport_state_t _state; #if Z_FEATURE_AUTO_RECONNECT == 1 _z_transport_tasks_t _tasks; #endif } _z_transport_common_t; // Send function prototype typedef z_result_t (*_zp_f_send_tmsg)(_z_transport_common_t *self, const _z_transport_message_t *t_msg); typedef enum { _Z_PENDING_PEER_STATE_PENDING = 0, _Z_PENDING_PEER_STATE_DONE = 1, _Z_PENDING_PEER_STATE_FAILED = 2, } _z_pending_peer_state_t; typedef struct { _z_string_t _locator; _z_pending_peer_state_t _state; } _z_pending_peer_t; static inline void _z_pending_peer_clear(_z_pending_peer_t *peer) { _z_string_clear(&peer->_locator); } _Z_ELEM_DEFINE(_z_pending_peer, _z_pending_peer_t, _z_noop_size, _z_pending_peer_clear, _z_noop_copy, _z_noop_move, _z_noop_eq, _z_noop_cmp, _z_noop_hash) _Z_SVEC_DEFINE_NO_COPY(_z_pending_peer, _z_pending_peer_t) typedef struct { _z_pending_peer_svec_t _peers; int32_t _timeout_ms; z_clock_t _start; uint32_t _sleep_ms; } _z_pending_peers_t; _z_pending_peers_t _z_pending_peers_null(void); z_result_t _z_pending_peers_copy_from_locators(_z_pending_peers_t *pending_peers, const _z_string_svec_t *locators); bool _z_pending_peers_has_pending(const _z_pending_peers_t *pending_peers); void _z_pending_peers_clear(_z_pending_peers_t *pending_peers); void _z_pending_peers_move(_z_pending_peers_t *dst, _z_pending_peers_t *src); typedef struct { _z_transport_common_t _common; // Known valid peers _z_transport_peer_unicast_slist_t *_peers; _z_pending_peers_t _pending_peers; } _z_transport_unicast_t; #define _Z_MULTICAST_ADDR_BUFF_SIZE 32 // Arbitrary size that must be able to contain any link address. typedef struct _z_transport_multicast_t { _z_transport_common_t _common; // Persistent source address associated with the current contents of _zbuf. // Required because datagram data may remain buffered across reads. uint8_t _zbuf_addr_buf[_Z_MULTICAST_ADDR_BUFF_SIZE]; _z_slice_t _zbuf_addr; // Known valid peers _z_transport_peer_multicast_slist_t *_peers; // T message send function _zp_f_send_tmsg _send_f; } _z_transport_multicast_t; typedef enum { _Z_TRANSPORT_UNICAST_TYPE, _Z_TRANSPORT_MULTICAST_TYPE, _Z_TRANSPORT_RAWETH_TYPE, _Z_TRANSPORT_NONE } _z_transport_type_t; typedef struct { union { _z_transport_unicast_t _unicast; _z_transport_multicast_t _multicast; _z_transport_multicast_t _raweth; } _transport; _z_transport_type_t _type; } _z_transport_t; typedef struct { _z_id_t _remote_zid; uint16_t _batch_size; _z_zint_t _initial_sn_rx; _z_zint_t _initial_sn_tx; _z_zint_t _lease; z_whatami_t _remote_whatami; uint8_t _key_id_res; uint8_t _req_id_res; uint8_t _seq_num_res; bool _is_qos; #if Z_FEATURE_FRAGMENTATION == 1 uint8_t _patch; #endif } _z_transport_unicast_establish_param_t; typedef struct { _z_conduit_sn_list_t _initial_sn_tx; uint8_t _seq_num_res; } _z_transport_multicast_establish_param_t; z_result_t _z_transport_peer_unicast_add(_z_transport_unicast_t *ztu, _z_transport_unicast_establish_param_t *param, _z_sys_net_socket_t socket, bool owns_socket, _z_transport_peer_unicast_t **output_peer); _z_transport_common_t *_z_transport_get_common(_z_transport_t *zt); size_t _z_transport_get_peers_count(_z_transport_t *zt); z_result_t _z_transport_close(_z_transport_t *zt, uint8_t reason); void _z_transport_clear(_z_transport_t *zt); void _z_transport_free(_z_transport_t **zt); static inline void _z_transport_get_link_properties(const _z_transport_common_t *transport, uint16_t *mtu, bool *is_streamed, bool *is_reliable) { *mtu = 0; *is_streamed = false; *is_reliable = false; if (transport != NULL && transport->_link != NULL) { *mtu = transport->_link->_mtu; *is_streamed = transport->_link->_cap._flow == Z_LINK_CAP_FLOW_STREAM; *is_reliable = transport->_link->_cap._is_reliable; } } #if Z_FEATURE_BATCHING == 1 z_result_t _z_transport_start_batching(_z_transport_t *zt); z_result_t _z_transport_stop_batching(_z_transport_t *zt); static inline bool _z_transport_batch_hold_tx_mutex(void) { #if Z_FEATURE_BATCH_TX_MUTEX == 1 return true; #else return false; #endif } static inline bool _z_transport_batch_hold_peer_mutex(void) { #if Z_FEATURE_BATCH_PEER_MUTEX == 1 return true; #else return false; #endif } #endif // Z_FEATURE_BATCHING == 1 #if Z_FEATURE_MULTI_THREAD == 1 static inline z_result_t _z_transport_tx_mutex_lock(_z_transport_common_t *ztc, bool block) { if (block) { _z_mutex_lock(&ztc->_mutex_tx); return _Z_RES_OK; } else { return _z_mutex_try_lock(&ztc->_mutex_tx); } } static inline void _z_transport_tx_mutex_unlock(_z_transport_common_t *ztc) { _z_mutex_unlock(&ztc->_mutex_tx); } static inline void _z_transport_peer_mutex_lock(_z_transport_common_t *ztc) { (void)_z_mutex_rec_lock(&ztc->_mutex_peer); } static inline void _z_transport_peer_mutex_unlock(_z_transport_common_t *ztc) { (void)_z_mutex_rec_unlock(&ztc->_mutex_peer); } #else static inline z_result_t _z_transport_tx_mutex_lock(_z_transport_common_t *ztc, bool block) { _ZP_UNUSED(ztc); _ZP_UNUSED(block); return _Z_RES_OK; } static inline void _z_transport_tx_mutex_unlock(_z_transport_common_t *ztc) { _ZP_UNUSED(ztc); } static inline void _z_transport_peer_mutex_lock(_z_transport_common_t *ztc) { _ZP_UNUSED(ztc); } static inline void _z_transport_peer_mutex_unlock(_z_transport_common_t *ztc) { _ZP_UNUSED(ztc); } #endif // Z_FEATURE_MULTI_THREAD == 1 #ifdef __cplusplus } #endif #endif /* INCLUDE_ZENOH_PICO_TRANSPORT_TRANSPORT_H */ ================================================ FILE: include/zenoh-pico/transport/unicast/accept.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_ACCEPT_H #define ZENOH_PICO_UNICAST_ACCEPT_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_UNICAST_PEER == 1 && \ (Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1) _z_fut_fn_result_t _zp_unicast_accept_task_fn(void *ztu_arg, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UNICAST_ACCEPT_H */ ================================================ FILE: include/zenoh-pico/transport/unicast/lease.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_LEASE_H #define ZENOH_PICO_UNICAST_LEASE_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_UNICAST_TRANSPORT == 1 z_result_t _zp_unicast_send_keep_alive(_z_transport_unicast_t *ztu); _z_fut_fn_result_t _zp_unicast_lease_task_fn(void *ztu_arg, _z_executor_t *executor); _z_fut_fn_result_t _zp_unicast_keep_alive_task_fn(void *ztu_arg, _z_executor_t *executor); _z_fut_fn_result_t _zp_unicast_failed_result(_z_transport_unicast_t *ztu, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_LINK_TASK_LEASE_H */ ================================================ FILE: include/zenoh-pico/transport/unicast/read.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_READ_H #define ZENOH_PICO_UNICAST_READ_H #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_UNICAST_TRANSPORT == 1 z_result_t _zp_unicast_read(_z_transport_unicast_t *ztu, bool single_read); _z_fut_fn_result_t _zp_unicast_read_task_fn(void *ztu_arg, _z_executor_t *executor); #endif #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UNICAST_READ_H */ ================================================ FILE: include/zenoh-pico/transport/unicast/rx.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_RX_H #define ZENOH_PICO_UNICAST_RX_H #include "zenoh-pico/transport/transport.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_unicast_recv_t_msg(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg); z_result_t _z_unicast_handle_transport_message(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg, _z_transport_peer_unicast_t *peer); z_result_t _z_unicast_update_rx_buffer(_z_transport_unicast_t *ztu); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UNICAST_RX_H */ ================================================ FILE: include/zenoh-pico/transport/unicast/transport.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_TRANSPORT_H #define ZENOH_PICO_UNICAST_TRANSPORT_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif z_result_t _z_unicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_unicast_establish_param_t *param); z_result_t _z_unicast_handshake_listen(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, z_whatami_t mode, _z_sys_net_socket_t *socket); z_result_t _z_unicast_open_client(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid); z_result_t _z_unicast_open_peer(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, int peer_op, _z_sys_net_socket_t *socket); z_result_t _z_unicast_send_close(_z_transport_unicast_t *ztu, uint8_t reason, bool link_only); z_result_t _z_unicast_transport_close(_z_transport_unicast_t *ztu, uint8_t reason); void _z_unicast_transport_clear(_z_transport_unicast_t *ztu); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UNICAST_TRANSPORT_H */ ================================================ FILE: include/zenoh-pico/transport/unicast.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UNICAST_H #define ZENOH_PICO_UNICAST_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif void _zp_unicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback); void _zp_unicast_info_session(const _z_transport_t *zt, _z_config_t *ps, int mode); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UNICAST_H */ ================================================ FILE: include/zenoh-pico/transport/utils.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_TRANSPORT_UTILS_H #define ZENOH_PICO_TRANSPORT_UTILS_H #include #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/transport.h" #ifdef __cplusplus extern "C" { #endif /*------------------ SN helpers ------------------*/ _z_zint_t _z_sn_max(uint8_t bits); _z_zint_t _z_sn_half(_z_zint_t sn); _z_zint_t _z_sn_modulo_mask(uint8_t bits); bool _z_sn_precedes(const _z_zint_t sn_resolution, const _z_zint_t sn_left, const _z_zint_t sn_right); bool _z_sn_consecutive(const _z_zint_t sn_resolution, const _z_zint_t sn_left, const _z_zint_t sn_right); _z_zint_t _z_sn_increment(const _z_zint_t sn_resolution, const _z_zint_t sn); _z_zint_t _z_sn_decrement(const _z_zint_t sn_resolution, const _z_zint_t sn); void _z_conduit_sn_list_copy(_z_conduit_sn_list_t *dst, const _z_conduit_sn_list_t *src); void _z_conduit_sn_list_decrement(const _z_zint_t sn_resolution, _z_conduit_sn_list_t *sns); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_TRANSPORT_UTILS_H */ ================================================ FILE: include/zenoh-pico/utils/checksum.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_CHECKSUM_H #define ZENOH_PICO_UTILS_CHECKSUM_H #include #include #ifdef __cplusplus extern "C" { #endif uint32_t _z_crc32(const uint8_t *message, size_t len); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_CHECKSUM_H */ ================================================ FILE: include/zenoh-pico/utils/config.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_PROPERTY_H #define ZENOH_PICO_UTILS_PROPERTY_H #include #include "zenoh-pico/collections/intmap.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/result.h" #ifdef __cplusplus extern "C" { #endif // Properties returned by _z_info() #define Z_INFO_PID_KEY 0x00 #define Z_INFO_PEER_PID_KEY 0x01 #define Z_INFO_ROUTER_PID_KEY 0x02 /** * Zenoh-net properties are represented as int-string map. */ typedef _z_str_intmap_t _z_config_t; /** * Initialize a new empty map of properties. */ z_result_t _z_config_init(_z_config_t *ps); /** * Insert a property with a given key to a properties map. * If a property with the same key already exists in the properties map, it is replaced. * * Parameters: * ps: A pointer to the properties map. * key: The key of the property to add. * value: The value of the property to add. */ z_result_t _zp_config_insert(_z_config_t *ps, uint8_t key, const char *value); z_result_t _zp_config_insert_string(_z_config_t *ps, uint8_t key, const _z_string_t *value); /** * Get the property with the given key from a properties map. * * Parameters: * ps: A pointer to properties map. * key: The key of the property. * * Returns: * The value of the property with key ``key`` in properties map ``ps``. */ char *_z_config_get(const _z_config_t *ps, uint8_t key); z_result_t _z_config_get_all(const _z_config_t *ps, _z_string_svec_t *locators, uint8_t key); /** * Retrieve a signed 32-bit integer property from the configuration. * If the property is not present, the provided default value is used. * * The value is parsed as a base-10 integer and validated to ensure it * fits within the range of int32_t. * * Parameters: * config: A pointer to the configuration object. * key: The key of the property to retrieve. * default_val: The default value to use if the property is not present. * out: A pointer to store the parsed result. */ z_result_t _z_config_get_i32_default(_z_config_t *config, uint8_t key, const char *default_val, int32_t *out); /** * Retrieve a boolean property from the configuration. * If the property is not present, the provided default value is used. * * Accepted values are "true" and "false". * * Parameters: * config: A pointer to the configuration object. * key: The key of the property to retrieve. * default_val: The default value to use if the property is not present. * out: A pointer to store the parsed result. */ z_result_t _z_config_get_bool_default(_z_config_t *config, uint8_t key, const char *default_val, bool *out); /** * Get the length of the given properties map. * * Parameters: * ps: A pointer to the properties map. * * Returns: * The length of the given properties map. */ #define _z_config_len _z_str_intmap_len /** * Clone a config. * * Parameters: * m: A pointer to the config to clone. * * Returns: * The clone of the config. */ #define _z_config_clone _z_str_intmap_clone /** * Get the length of the given properties map. * * Parameters: * ps: A pointer to the properties map. * * Returns: * A boolean to indicate if properties are present. */ #define _z_config_is_empty _z_str_intmap_is_empty /** * Clear a set of properties. * * Parameters: * ps: A pointer to the properties map. */ #define _z_config_clear _z_str_intmap_clear /** * Free a set of properties. * * Parameters: * ps: A pointer to a pointer of properties. * */ #define _z_config_free _z_str_intmap_free #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_PROPERTY_H */ ================================================ FILE: include/zenoh-pico/utils/encoding.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_ENCODING_H #define ZENOH_PICO_UTILS_ENCODING_H #include #include #ifdef __cplusplus extern "C" { #endif size_t _z_cobs_encode(const uint8_t *input, size_t input_len, uint8_t *output); size_t _z_cobs_decode(const uint8_t *input, size_t input_len, uint8_t *output); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_ENCODING_H */ ================================================ FILE: include/zenoh-pico/utils/endianness.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_ENDIANNESS_H #define ZENOH_PICO_UTILS_ENDIANNESS_H #include #include #ifdef __cplusplus extern "C" { #endif #if !defined(ZENOH_ENDIANNNESS_BIG) && !defined(ZENOH_ENDIANNNESS_LITTLE) // Gcc/clang test #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define ZENOH_ENDIANNNESS_BIG #else #define ZENOH_ENDIANNNESS_LITTLE #endif #endif // Load int from memory with specified endianness // *** Little endian *** #define _Z_LE_LOAD_IMPL(SIZE) \ static inline uint##SIZE##_t _z_le_load##SIZE(const uint8_t *src) { \ uint##SIZE##_t val = 0; \ for (size_t i = sizeof(val); i != 0; --i) { \ val = val << 8; \ val = (uint##SIZE##_t)(val | src[i - 1]); \ } \ return val; \ } #define _Z_LE_STORE_IMPL(SIZE) \ static inline void _z_le_store##SIZE(uint##SIZE##_t val, uint8_t *dst) { \ for (size_t i = 0; i < sizeof(val); ++i) { \ dst[i] = (uint8_t)val; \ val = val >> 8; \ } \ } _Z_LE_LOAD_IMPL(16) _Z_LE_LOAD_IMPL(32) _Z_LE_LOAD_IMPL(64) _Z_LE_STORE_IMPL(16) _Z_LE_STORE_IMPL(32) _Z_LE_STORE_IMPL(64) #undef _Z_LE_LOAD_IMPL #undef _Z_LE_STORE_IMPL // *** Big endian *** #define _Z_BE_LOAD_IMPL(SIZE) \ static inline uint##SIZE##_t _z_be_load##SIZE(const uint8_t *src) { \ uint##SIZE##_t val = 0; \ for (size_t i = 0; i < sizeof(val); ++i) { \ val = val << 8; \ val = (uint##SIZE##_t)(val | src[i]); \ } \ return val; \ } #define _Z_BE_STORE_IMPL(SIZE) \ static inline void _z_be_store##SIZE(uint##SIZE##_t val, uint8_t *dst) { \ for (size_t i = sizeof(val); i != 0; --i) { \ dst[i - 1] = (uint8_t)val; \ val = val >> 8; \ } \ } _Z_BE_LOAD_IMPL(16) _Z_BE_LOAD_IMPL(32) _Z_BE_LOAD_IMPL(64) _Z_BE_STORE_IMPL(16) _Z_BE_STORE_IMPL(32) _Z_BE_STORE_IMPL(64) #undef _Z_BE_LOAD_IMPL #undef _Z_BE_STORE_IMPL // *** Host *** static inline uint16_t _z_host_le_load8(const uint8_t *src) { return src[0]; } static inline uint16_t _z_host_le_load16(const uint8_t *src) { #if defined(ZENOH_ENDIANNNESS_BIG) return _z_be_load16(src); #elif defined(ZENOH_ENDIANNNESS_LITTLE) return _z_le_load16(src); #endif } static inline uint32_t _z_host_le_load32(const uint8_t *src) { #if defined(ZENOH_ENDIANNNESS_BIG) return _z_be_load32(src); #elif defined(ZENOH_ENDIANNNESS_LITTLE) return _z_le_load32(src); #endif } static inline uint64_t _z_host_le_load64(const uint8_t *src) { #if defined(ZENOH_ENDIANNNESS_BIG) return _z_be_load64(src); #elif defined(ZENOH_ENDIANNNESS_LITTLE) return _z_le_load64(src); #endif } static inline void _z_host_le_store8(const uint8_t val, uint8_t *dst) { dst[0] = val; } static inline void _z_host_le_store16(const uint16_t val, uint8_t *dst) { #if defined(ZENOH_ENDIANNNESS_BIG) _z_be_store16(val, dst); #elif defined(ZENOH_ENDIANNNESS_LITTLE) _z_le_store16(val, dst); #endif } static inline void _z_host_le_store32(const uint32_t val, uint8_t *dst) { #if defined(ZENOH_ENDIANNNESS_BIG) _z_be_store32(val, dst); #elif defined(ZENOH_ENDIANNNESS_LITTLE) _z_le_store32(val, dst); #endif } static inline void _z_host_le_store64(const uint64_t val, uint8_t *dst) { #if defined(ZENOH_ENDIANNNESS_BIG) _z_be_store64(val, dst); #elif defined(ZENOH_ENDIANNNESS_LITTLE) _z_le_store64(val, dst); #endif } static inline void _z_host_u16_from_le_u16(uint16_t *val) { #if defined(ZENOH_ENDIANNNESS_BIG) uint16_t tmp = (uint16_t)((*val & 0x00FF) << 8) + (uint16_t)((*val & 0xFF00) >> 8); *val = tmp; #elif defined(ZENOH_ENDIANNNESS_LITTLE) _ZP_UNUSED(val); return; #endif } // Return u16 individual bytes static inline uint8_t _z_get_u16_lsb(uint_fast16_t val) { return (uint8_t)(val >> 0); } static inline uint8_t _z_get_u16_msb(uint_fast16_t val) { return (uint8_t)(val >> 8); } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_ENDIANNESS_H */ ================================================ FILE: include/zenoh-pico/utils/hash.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_HASH_H #define ZENOH_PICO_UTILS_HASH_H #include #ifdef __cplusplus extern "C" { #endif // Define FNV1a parameters for 32 and 64-bit platforms #if SIZE_MAX == UINT64_MAX #define _Z_FNV_OFFSET_BASIS 14695981039346656037ULL #define _Z_FNV_PRIME 1099511628211ULL #elif SIZE_MAX == UINT32_MAX #define _Z_FNV_OFFSET_BASIS 2166136261U #define _Z_FNV_PRIME 16777619U #else #error "Unsupported size_t size" #endif static inline size_t _z_hash_combine(size_t h1, size_t h2) { h1 ^= h2; h1 *= _Z_FNV_PRIME; return h1; } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_HASH_H */ ================================================ FILE: include/zenoh-pico/utils/json_encoder.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_JSON_ENCODER_H #define ZENOH_PICO_UTILS_JSON_ENCODER_H #include "zenoh-pico/api/types.h" #ifdef __cplusplus extern "C" { #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADMIN_SPACE == 1 typedef enum { _Z_JSON_CTX_OBJ = 1, _Z_JSON_CTX_ARR = 2, } _z_json_ctx_kind_t; typedef struct { _z_json_ctx_kind_t kind; bool expect_value; bool first; } _z_json_frame_t; // Maximum nesting depth supported by the JSON encoder #ifndef Z_JSON_MAX_DEPTH #define Z_JSON_MAX_DEPTH 16 #endif typedef struct { z_owned_bytes_writer_t _bw; uint8_t _depth; _z_json_frame_t _stk[Z_JSON_MAX_DEPTH]; bool _done; } _z_json_encoder_t; z_result_t _z_json_encoder_empty(_z_json_encoder_t *je); z_result_t _z_json_encoder_start_object(_z_json_encoder_t *je); z_result_t _z_json_encoder_end_object(_z_json_encoder_t *je); z_result_t _z_json_encoder_start_array(_z_json_encoder_t *je); z_result_t _z_json_encoder_end_array(_z_json_encoder_t *je); z_result_t _z_json_encoder_write_key(_z_json_encoder_t *je, const char *key); z_result_t _z_json_encoder_write_string(_z_json_encoder_t *je, const char *value); z_result_t _z_json_encoder_write_z_string(_z_json_encoder_t *je, const _z_string_t *value); z_result_t _z_json_encoder_write_z_slice(_z_json_encoder_t *je, const _z_slice_t *value); z_result_t _z_json_encoder_write_double(_z_json_encoder_t *je, double value); z_result_t _z_json_encoder_write_i64(_z_json_encoder_t *je, int64_t value); z_result_t _z_json_encoder_write_u64(_z_json_encoder_t *je, uint64_t value); z_result_t _z_json_encoder_write_boolean(_z_json_encoder_t *je, bool value); z_result_t _z_json_encoder_write_null(_z_json_encoder_t *je); z_result_t _z_json_encoder_finish(_z_json_encoder_t *je, z_owned_bytes_t *bytes); void _z_json_encoder_clear(_z_json_encoder_t *je); #endif // Z_FEATURE_ADMIN_SPACE == 1 #endif // Z_FEATURE_UNSTABLE_API #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_JSON_ENCODER_H */ ================================================ FILE: include/zenoh-pico/utils/locality.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_LOCALITY_H #define ZENOH_PICO_UTILS_LOCALITY_H #include "zenoh-pico/api/constants.h" static inline bool _z_locality_allows_local(z_locality_t locality) { switch (locality) { case Z_LOCALITY_REMOTE: return false; case Z_LOCALITY_ANY: case Z_LOCALITY_SESSION_LOCAL: default: return true; } } static inline bool _z_locality_allows_remote(z_locality_t locality) { switch (locality) { case Z_LOCALITY_SESSION_LOCAL: return false; case Z_LOCALITY_ANY: case Z_LOCALITY_REMOTE: default: return true; } } #endif // ZENOH_PICO_UTILS_LOCALITY_H ================================================ FILE: include/zenoh-pico/utils/logging.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_LOGGING_H #define ZENOH_PICO_UTILS_LOGGING_H #include #include "zenoh-pico/system/common/platform.h" #ifdef __cplusplus extern "C" { #endif // Allows replacement of printf on targets that don't support it #ifndef ZENOH_LOG_PRINT #define ZENOH_LOG_PRINT printf #endif // Compatibility with legacy logging system where // 1 -> ERROR // 2 -> INFO // 3 -> DEBUG #ifdef ZENOH_DEBUG #undef ZENOH_LOG_ERROR #undef ZENOH_LOG_WARN #undef ZENOH_LOG_INFO #undef ZENOH_LOG_DEBUG #undef ZENOH_LOG_TRACE #if ZENOH_DEBUG == 1 #define ZENOH_LOG_ERROR #elif ZENOH_DEBUG == 2 #define ZENOH_LOG_INFO #elif ZENOH_DEBUG == 3 #define ZENOH_LOG_DEBUG #endif #endif // Logging macros #define _Z_LOG(level, ...) \ do { \ char __timestamp[64]; \ z_time_now_as_str(__timestamp, sizeof(__timestamp)); \ ZENOH_LOG_PRINT("[%s " #level " ::%s] ", __timestamp, __func__); \ ZENOH_LOG_PRINT(__VA_ARGS__); \ ZENOH_LOG_PRINT("\r\n"); \ } while (false) #define _Z_LOG_NONL(level, ...) \ do { \ char __timestamp[64]; \ z_time_now_as_str(__timestamp, sizeof(__timestamp)); \ ZENOH_LOG_PRINT("[%s " #level " ::%s] ", __timestamp, __func__); \ ZENOH_LOG_PRINT(__VA_ARGS__); \ } while (false) // In debug build, if a level is not enabled, the following macro is used instead // in order to check that the arguments are valid and compile fine. #define _Z_CHECK_LOG(...) \ do { \ if (false) ZENOH_LOG_PRINT(__VA_ARGS__); \ } while (false) #ifdef ZENOH_LOG_TRACE #define ZENOH_LOG_DEBUG #define _Z_TRACE(...) _Z_LOG(TRACE, __VA_ARGS__) #elif defined(Z_BUILD_LOG) #define _Z_TRACE _Z_CHECK_LOG #else #define _Z_TRACE(...) (void)(0) #endif #ifdef ZENOH_LOG_DEBUG #define ZENOH_LOG_INFO #define _Z_DEBUG(...) _Z_LOG(DEBUG, __VA_ARGS__) #define _Z_DEBUG_NONL(...) _Z_LOG_NONL(DEBUG, __VA_ARGS__) #elif defined(Z_BUILD_LOG) #define _Z_DEBUG _Z_CHECK_LOG #define _Z_DEBUG_NONL _Z_CHECK_LOG #else #define _Z_DEBUG(...) (void)(0) #define _Z_DEBUG_NONL(...) (void)(0) #endif #ifdef ZENOH_LOG_INFO #define ZENOH_LOG_WARN #define _Z_INFO(...) _Z_LOG(INFO, __VA_ARGS__) #elif defined(Z_BUILD_LOG) #define _Z_INFO _Z_CHECK_LOG #else #define _Z_INFO(...) (void)(0) #endif #ifdef ZENOH_LOG_WARN #define ZENOH_LOG_ERROR #define _Z_WARN(...) _Z_LOG(WARN, __VA_ARGS__) #elif defined(Z_BUILD_LOG) #define _Z_WARN _Z_CHECK_LOG #else #define _Z_WARN(...) (void)(0) #endif #ifdef ZENOH_LOG_ERROR #define _Z_ERROR(...) _Z_LOG(ERROR, __VA_ARGS__) #elif defined(Z_BUILD_LOG) #define _Z_ERROR _Z_CHECK_LOG #else #define _Z_ERROR(...) (void)(0) #endif // Error logging macros // Potentially add a function call to a logger if needs arise #define _Z_ERROR_RETURN(err_code) \ do { \ _Z_TRACE("Error generated: %d, at %s:%d", err_code, __FILE__, __LINE__); \ return err_code; \ } while (false) #define _Z_ERROR_LOG(err_code) \ do { \ _Z_TRACE("Error generated: %d, at %s:%d", err_code, __FILE__, __LINE__); \ } while (false) #ifdef __cplusplus } #endif #endif // ZENOH_PICO_UTILS_LOGGING_H ================================================ FILE: include/zenoh-pico/utils/mutex.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_MUTEX_H #define ZENOH_PICO_UTILS_MUTEX_H #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #ifdef __cplusplus extern "C" { #endif #if Z_FEATURE_MULTI_THREAD == 1 static inline z_result_t _z_mutex_rec_mt_lock(_z_mutex_rec_t *m) { return _z_mutex_rec_lock(m); } static inline z_result_t _z_mutex_rec_mt_unlock(_z_mutex_rec_t *m) { return _z_mutex_rec_unlock(m); } #else static inline z_result_t _z_mutex_rec_mt_lock(_z_mutex_rec_t *m) { _ZP_UNUSED(m); return _Z_RES_OK; } static inline z_result_t _z_mutex_rec_mt_unlock(_z_mutex_rec_t *m) { _ZP_UNUSED(m); return _Z_RES_OK; } #endif #ifdef __cplusplus } #endif #endif ================================================ FILE: include/zenoh-pico/utils/pointers.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_POINTERS_H #define ZENOH_PICO_UTILS_POINTERS_H #include #include #ifdef __cplusplus extern "C" { #endif /** * Computes the distance between two ``uint8_t`` pointers as an absolute value. * Note that ``l_ptr`` must be higher than ``r_ptr``. * * Parameters: * l_ptr: The first pointer. * r_ptr: The second pointer. * * Returns: * Returns the distance between the pointers as an absolute value. */ static inline size_t _z_ptr_u8_diff(const uint8_t *l_ptr, const uint8_t *r_ptr) { return (size_t)(l_ptr - r_ptr); } /** * Offsets a ``uint8_t`` pointer by a given distance. Offsets can be both positive and negative values. * * Parameters: * ptr: The pointer to offset. * off: The offset distance to be applied. * * Returns: * Returns a ``const uint8_t`` pointer, pointing to the offset position. */ const uint8_t *_z_cptr_u8_offset(const uint8_t *ptr, ptrdiff_t off); /** * Offsets a ``uint8_t`` pointer by a given distance. Offsets can be both positive and negative values. * * Parameters: * ptr: The pointer to offset. * off: The offset distance to be applied. * * Returns: * Returns a ``uint8_t`` pointer, pointing to the offset position. */ static inline uint8_t *_z_ptr_u8_offset(uint8_t *ptr, const ptrdiff_t off) { return ptr + off; } /** * Computes the distance between two ``char`` pointers as an absolute value. * Note that ``l_ptr`` must be higher than ``r_ptr``. * * Parameters: * l_ptr: The first pointer. * r_ptr: The second pointer. * * Returns: * Returns the distance between the pointers as an absolute value. */ static inline size_t _z_ptr_char_diff(const char *l_ptr, const char *r_ptr) { return (size_t)(l_ptr - r_ptr); } /** * Offsets a ``char`` pointer by a given distance. Offsets can be both positive and negative values. * * Parameters: * ptr: The pointer to offset. * off: The offset distance to be applied. * * Returns: * Returns a ``const char`` pointer, pointing to the offset position. */ const char *_z_cptr_char_offset(const char *ptr, ptrdiff_t off); /** * Offsets a ``char`` pointer by a given distance. Offsets can be both positive and negative values. * * Parameters: * ptr: The pointer to offset. * off: The offset distance to be applied. * * Returns: * Returns a ``char`` pointer, pointing to the offset position. */ static inline char *_z_ptr_char_offset(char *ptr, const ptrdiff_t off) { return ptr + off; } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_POINTERS_H */ ================================================ FILE: include/zenoh-pico/utils/query_params.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_QUERY_PARAMS_H #define ZENOH_PICO_UTILS_QUERY_PARAMS_H #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/utils/string.h" #ifdef __cplusplus extern "C" { #endif #define _Z_QUERY_PARAMS_KEY_TIME "_time" #define _Z_QUERY_PARAMS_KEY_TIME_LEN (sizeof(_Z_QUERY_PARAMS_KEY_TIME) - 1) #define _Z_QUERY_PARAMS_KEY_RANGE "_sn" #define _Z_QUERY_PARAMS_KEY_RANGE_LEN (sizeof(_Z_QUERY_PARAMS_KEY_RANGE) - 1) #define _Z_QUERY_PARAMS_KEY_MAX "_max" #define _Z_QUERY_PARAMS_KEY_MAX_LEN (sizeof(_Z_QUERY_PARAMS_KEY_MAX) - 1) #define _Z_QUERY_PARAMS_KEY_ANYKE "_anyke" #define _Z_QUERY_PARAMS_KEY_ANYKE_LEN (sizeof(_Z_QUERY_PARAMS_KEY_ANYKE) - 1) #define _Z_QUERY_PARAMS_LIST_SEPARATOR ";" #define _Z_QUERY_PARAMS_LIST_SEPARATOR_LEN (sizeof(_Z_QUERY_PARAMS_LIST_SEPARATOR) - 1) #define _Z_QUERY_PARAMS_FIELD_SEPARATOR "=" #define _Z_QUERY_PARAMS_FIELD_SEPARATOR_LEN (sizeof(_Z_QUERY_PARAMS_FIELD_SEPARATOR) - 1) typedef struct { _z_str_se_t key; _z_str_se_t value; } _z_query_param_t; typedef struct { bool _has_start; uint32_t _start; bool _has_end; uint32_t _end; } _z_query_param_range_t; /** * Extracts the next query parameter from a `_z_str_se_t` string. * * Returns a `_z_query_param_t` with positions of the next key/value in the string if present. * After invocation `str` will point to the remainder of the string. */ _z_query_param_t _z_query_params_next(_z_str_se_t *str); bool _z_parameters_has_anyke(const char *parameters, size_t parameters_len); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_QUERY_PARAMS_H */ ================================================ FILE: include/zenoh-pico/utils/result.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_RESULT_H #define ZENOH_PICO_UTILS_RESULT_H #include #ifdef __cplusplus extern "C" { #endif #define _ZP_UNUSED(x) (void)(x) #define _ZP_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #define _Z_ERR_MESSAGE_MASK 0x88 #define _Z_ERR_ENTITY_MASK 0x90 #define _Z_ERR_TRANSPORT_MASK 0x98 #define _Z_ERR_CONFIG_MASK 0xa0 #define _Z_ERR_SCOUT_MASK 0xa8 #define _Z_ERR_SYSTEM_MASK 0xb0 #define _Z_ERR_GENERIC_MASK 0xb8 typedef int8_t z_result_t; /*------------------ Result Enums ------------------*/ typedef enum { _Z_RES_OK = 0, Z_OK = 0, _Z_RES_CHANNEL_CLOSED = 1, Z_CHANNEL_DISCONNECTED = 1, _Z_RES_CHANNEL_NODATA = 2, Z_CHANNEL_NODATA = 2, _Z_NO_DATA_PROCESSED = 3, Z_NO_DATA_PROCESSED = 3, _Z_RESOURCE_POSITIVE_REF_COUNT = 4, Z_SYNC_GROUP_CLOSED = 6, _Z_ERR_FAILED_TO_SPAWN_TASK = -121, _Z_ERR_TRANSPORT_RX_DURATION_EXPIRED = -120, _Z_ERR_MESSAGE_DESERIALIZATION_FAILED = -119, _Z_ERR_MESSAGE_SERIALIZATION_FAILED = -118, _Z_ERR_MESSAGE_UNEXPECTED = -117, _Z_ERR_MESSAGE_FLAG_UNEXPECTED = -116, _Z_ERR_MESSAGE_ZENOH_DECLARATION_UNKNOWN = -115, _Z_ERR_MESSAGE_ZENOH_UNKNOWN = -114, _Z_ERR_MESSAGE_TRANSPORT_UNKNOWN = -113, _Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN = -112, _Z_ERR_ENTITY_DECLARATION_FAILED = -111, _Z_ERR_ENTITY_UNKNOWN = -110, _Z_ERR_KEYEXPR_UNKNOWN = -109, _Z_ERR_KEYEXPR_NOT_MATCH = -108, _Z_ERR_QUERY_NOT_MATCH = -107, _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY = -104, _Z_ERR_TRANSPORT_NOT_AVAILABLE = -103, _Z_ERR_TRANSPORT_OPEN_FAILED = -102, _Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION = -101, _Z_ERR_TRANSPORT_TX_FAILED = -100, _Z_ERR_TRANSPORT_RX_FAILED = -99, _Z_ERR_TRANSPORT_NO_SPACE = -98, _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES = -97, _Z_ERR_CONFIG_FAILED_INSERT = -95, _Z_ERR_CONFIG_UNSUPPORTED_CLIENT_MULTICAST = -94, _Z_ERR_CONFIG_UNSUPPORTED_PEER_UNICAST = -93, _Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN = -92, _Z_ERR_CONFIG_LOCATOR_INVALID = -91, _Z_ERR_CONFIG_INVALID_MODE = -90, _Z_ERR_CONFIG_INVALID_VALUE = -89, _Z_ERR_SCOUT_NO_RESULTS = -87, _Z_ERR_UNDERFLOW = -81, _Z_ERR_SYSTEM_GENERIC = -80, _Z_ERR_SYSTEM_TASK_FAILED = -79, _Z_ERR_SYSTEM_OUT_OF_MEMORY = -78, _Z_ERR_CONNECTION_CLOSED = -77, _Z_ERR_DID_NOT_READ = -76, _Z_ERR_INVALID = -75, Z_EINVAL = -75, _Z_ERR_OVERFLOW = -74, _Z_ERR_SESSION_CLOSED = -73, Z_EDESERIALIZE = -72, Z_ETIMEDOUT = -71, _Z_ERR_KEYEXPR_DECLARED_ON_ANOTHER_SESSION = -70, Z_ERR_CANCELLED = -69, _Z_ERR_NULL = -127, _Z_ERR_GENERIC = -128 } _z_res_t; #define _Z_SET_IF_OK(expr, value) \ { \ if (expr == _Z_RES_OK) { \ expr = value; \ } \ } #define _Z_RETURN_IF_ERR(expr) \ { \ z_result_t __res = expr; \ if (__res != _Z_RES_OK) { \ return __res; \ } \ } #define _Z_CLEAN_RETURN_IF_ERR(base_expr, clean_expr) \ { \ z_result_t __res = base_expr; \ if (__res != _Z_RES_OK) { \ clean_expr; \ return __res; \ } \ } #define _Z_RETURN_ERR_OOM_IF_TRUE(expr) \ { \ if (expr) { \ return _Z_ERR_SYSTEM_OUT_OF_MEMORY; \ } \ } #define _Z_CLEAN_RETURN_ERR_OOM_IF_TRUE(expr, clean_expr) \ { \ if (expr) { \ clean_expr; \ return _Z_ERR_SYSTEM_OUT_OF_MEMORY; \ } \ } #define _Z_IS_OK(expr) (expr == _Z_RES_OK) #define _Z_IS_ERR(expr) (expr != _Z_RES_OK) #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_RESULT_H */ ================================================ FILE: include/zenoh-pico/utils/sleep.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef INCLUDE_ZENOH_PICO_UTILS_SLEEP_H #define INCLUDE_ZENOH_PICO_UTILS_SLEEP_H #include #include #include "zenoh-pico/system/platform.h" #define _Z_SLEEP_BACKOFF_MIN_MS 100 #define _Z_SLEEP_BACKOFF_MAX_MS 1000 static inline void _z_backoff_advance(uint32_t *sleep_ms) { if (*sleep_ms < _Z_SLEEP_BACKOFF_MAX_MS) { *sleep_ms <<= 1; if (*sleep_ms > _Z_SLEEP_BACKOFF_MAX_MS) { *sleep_ms = _Z_SLEEP_BACKOFF_MAX_MS; } } } static inline bool _z_backoff_sleep(z_clock_t *start, int32_t timeout_ms, uint32_t *sleep_ms) { uint32_t current_sleep_ms = *sleep_ms; if (timeout_ms == 0) { return false; } if (timeout_ms > 0) { unsigned long elapsed = z_clock_elapsed_ms(start); if (elapsed >= (unsigned long)timeout_ms) { return false; } uint32_t remaining_ms = (uint32_t)timeout_ms - (uint32_t)elapsed; if (current_sleep_ms > remaining_ms) { current_sleep_ms = remaining_ms; } } z_sleep_ms(current_sleep_ms); _z_backoff_advance(sleep_ms); return true; } #endif /* INCLUDE_ZENOH_PICO_UTILS_SLEEP_H */ ================================================ FILE: include/zenoh-pico/utils/string.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_STRING_H #define ZENOH_PICO_UTILS_STRING_H #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct { char const *start; char const *end; } _z_str_se_t; typedef struct { _z_str_se_t s; char const *delimiter; } _z_splitstr_t; /** * Creates a `_z_str_se_t` from a null-terminated C string. */ _z_str_se_t _z_bstrnew(const char *start); /** * The reverse equivalent of libc's `strstr`. * * Returns NULL if the needle is not found. * If found, the return pointer will point to the end of the last occurring * needle within the haystack. */ char const *_z_rstrstr(const char *haystack_start, const char *haystack_end, const char *needle); /** * A non-null-terminated haystack equivalent of libc's `strstr`. * * Returns NULL if the needle is not found. * If found, the return pointer will point to the start of the first occurrence * of the needle within the haystack. */ char const *_z_strstr(char const *haystack_start, char const *haystack_end, const char *needle_start); char const *_z_strstr_skipneedle(char const *haystack_start, char const *haystack_end, const char *needle_start); char const *_z_bstrstr_skipneedle(_z_str_se_t haystack, _z_str_se_t needle); bool _z_splitstr_is_empty(const _z_splitstr_t *src); _z_str_se_t _z_splitstr_next(_z_splitstr_t *str); _z_str_se_t _z_splitstr_split_once(_z_splitstr_t src, _z_str_se_t *next); _z_str_se_t _z_splitstr_nextback(_z_splitstr_t *str); size_t _z_strcnt(char const *haystack_start, const char *harstack_end, const char *needle_start); size_t _z_str_startswith(const char *s, const char *needle); // Must be used on a null terminated string with str[strlen(str) + 1] being valid memory. static inline void _z_str_append(char *str, const char c) { size_t len = strlen(str); str[len] = c; str[len + 1] = '\0'; } /* * Convert a non null terminated `_z_str_se_t` to a uint32_t. */ bool _z_str_se_atoui(const _z_str_se_t *str, uint32_t *result); /* * Parse a null-terminated base-10 string as a signed 32-bit integer. */ bool _z_str_parse_i32(const char *s, int32_t *out); /* * Parse a null-terminated boolean string. * * Accepted values are "true" and "false". */ bool _z_str_parse_bool(const char *s, bool *out); void *_z_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len); /* * Safely copies a block of memory into a destination buffer at a given offset, if bounds allow. * * Parameters: * dest - Pointer to the destination buffer. * dest_len - Total size of the destination buffer. * offset - Pointer to the current write offset; may be NULL to use offset 0 without updating, otherwise will be * updated if the copy succeeds. * src - Pointer to the source data. * len - Number of bytes to copy. * * Returns: * true - If the copy succeeds (bounds are respected and pointers are valid). * false - If the copy would overflow the buffer, or any pointer is NULL. */ static inline bool _z_memcpy_checked(void *dest, size_t dest_len, size_t *offset, const void *src, size_t len) { if (dest == NULL || src == NULL) { return false; } size_t local_offset = (offset != NULL) ? *offset : 0; if (len > dest_len - local_offset) { return false; } char *d_start = (char *)dest + local_offset; char *d_end = d_start + len; const char *s_start = (const char *)src; const char *s_end = s_start + len; // Check for overlap: if src and dest ranges intersect if ((s_start < d_end && s_end > d_start)) { return false; // Overlap detected } // SAFETY: Copy is bounds-checked above. // Flawfinder: ignore [CWE-120] memcpy(d_start, src, len); if (offset != NULL) { *offset += len; } return true; } #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_STRING_H */ ================================================ FILE: include/zenoh-pico/utils/time_range.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_UTILS_TIME_RANGE_H #define ZENOH_PICO_UTILS_TIME_RANGE_H #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/string.h" #ifdef __cplusplus extern "C" { #endif static const double _Z_TIME_RANGE_U_TO_SECS = 0.000001; static const double _Z_TIME_RANGE_MS_TO_SECS = 0.001; static const double _Z_TIME_RANGE_M_TO_SECS = 60.0; static const double _Z_TIME_RANGE_H_TO_SECS = 3600.0; static const double _Z_TIME_RANGE_D_TO_SECS = 86400.0; static const double _Z_TIME_RANGE_W_TO_SECS = 604800.0; typedef struct { enum { _Z_TIME_BOUND_INCLUSIVE, _Z_TIME_BOUND_EXCLUSIVE, _Z_TIME_BOUND_UNBOUNDED } bound; double now_offset; } _z_time_bound_t; typedef struct { _z_time_bound_t start; _z_time_bound_t end; } _z_time_range_t; /** * Parse a time range from a string. * * Returns true if the string contained a valid time range, false otherwise. * If valid range will contain the result. */ bool _z_time_range_from_str(const char *str, size_t len, _z_time_range_t *range); /** * Converts a time bound into a string. * * Returns true if the time bound was successfully converted, false otherwise. * If valid buf will contain the result. */ bool _z_time_bound_to_str(const _z_time_bound_t *bound, char *buf, size_t buf_len); /** * Converts a time range into a string. * * Returns true if the time range was successfully converted, false otherwise. * If valid buf will contain the result. */ bool _z_time_range_to_str(const _z_time_range_t *range, char *buf, size_t buf_len); /** * Resolves an offset to a base time. * * Returns the offset time. */ _z_ntp64_t _z_time_range_resolve_offset(_z_ntp64_t base, double offset); /** * Checks if a timestamp is contained within a time range at the specified time. * * Returns true if the timestamp is containend in the time range, false otherwise. */ bool _z_time_range_contains_at_time(const _z_time_range_t *range, const _z_ntp64_t timestamp, const _z_ntp64_t time); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_TIME_RANGE_H */ ================================================ FILE: include/zenoh-pico/utils/uuid.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/protocol/core.h" #ifndef ZENOH_PICO_UTILS_UUID_H #define ZENOH_PICO_UTILS_UUID_H #ifdef __cplusplus extern "C" { #endif /** * Converts an UUID in string format to a byte array. * * Parameters: * bytes: Pointer to an already allocated byte array of size (at least) 16 bytes. * uuid_str: A valid UUID string. */ void _z_uuid_to_bytes(uint8_t *bytes, const char *uuid_str); /** * Converts an Zenoh ID to string. * * Parameters: * id: Zenoh ID. * * Returns: * ID string representation */ _z_string_t _z_id_to_string(const _z_id_t *id); /** * Parses a string to a Zenoh ID. * * Parameters: * str - Pointer to the string to parse. * * Returns: * The parsed Zenoh ID if successful, or an empty ID on failure. */ _z_id_t _z_id_from_string(const _z_string_t *str); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_UTILS_UUID_H */ ================================================ FILE: include/zenoh-pico.h ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // ⚠️ This file is auto-generated from include/zenoh-pico.h.in #ifndef ZENOH_PICO_H #define ZENOH_PICO_H #define ZENOH_PICO "1.9.0" #define ZENOH_PICO_MAJOR 1 #define ZENOH_PICO_MINOR 9 #define ZENOH_PICO_PATCH 0 #define ZENOH_PICO_TWEAK 0 #include "zenoh-pico/api/admin_space.h" #include "zenoh-pico/api/advanced_publisher.h" #include "zenoh-pico/api/advanced_subscriber.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/encoding.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #endif /* ZENOH_PICO_H */ ================================================ FILE: include/zenoh-pico.h.in ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // ⚠️ This file is auto-generated from include/zenoh-pico.h.in #ifndef ZENOH_PICO_H #define ZENOH_PICO_H #define ZENOH_PICO "@ZENOH_PICO@" #define ZENOH_PICO_MAJOR @ZENOH_PICO_MAJOR@ #define ZENOH_PICO_MINOR @ZENOH_PICO_MINOR@ #define ZENOH_PICO_PATCH @ZENOH_PICO_PATCH@ #define ZENOH_PICO_TWEAK @ZENOH_PICO_TWEAK@ #include "zenoh-pico/api/admin_space.h" #include "zenoh-pico/api/advanced_publisher.h" #include "zenoh-pico/api/advanced_subscriber.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/encoding.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #endif /* ZENOH_PICO_H */ ================================================ FILE: library.json ================================================ { "name": "zenoh-pico", "version": "1.9.0", "description": "The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute. It unifies data in motion, data in-use, data at rest and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks. Zenoh-Pico is the implementation able to scale down to extremely constrainded devices and networks.", "keywords": [ "pubsub", "publish", "subscribe", "query", "distributed data distribution" ], "homepage": "https://zenoh.io/", "repository": { "type": "git", "url": "https://github.com/eclipse-zenoh/zenoh-pico" }, "authors": { "name": "ZettaScale Technology Zenoh Team", "email": "zenoh@zettascale.tech", "url": "https://www.zettascale.tech/", "maintainer": true }, "license": "Apache-2.0 OR EPL-2.0", "frameworks": [ "arduino", "espidf", "mbed", "zephyr" ], "headers": [ "zenoh-pico.h" ], "examples": [ "examples" ], "dependencies": { "BluetoothSerial": "0.16.1", "FreeRTOS": "1.0.0" }, "build": { "extraScript": "extra_script.py" } } ================================================ FILE: src/api/admin_space.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/admin_space.h" #include "zenoh-pico/api/encoding.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/json_encoder.h" #if Z_FEATURE_ADMIN_SPACE == 1 #define _ZE_ADMIN_SPACE_KE_BUF_LEN 256 typedef struct { const char *data; size_t len; } _ze_admin_space_ke_segment_t; typedef z_result_t (*_ze_admin_space_encode_fn_t)(_z_json_encoder_t *je, void *ctx); typedef z_result_t (*_ze_admin_space_ke_builder_t)(z_owned_keyexpr_t *ke, const z_id_t *zid); typedef struct { const char *name; _ze_admin_space_ke_builder_t build_ke; _ze_admin_space_encode_fn_t encode; } _ze_admin_space_endpoint_t; static z_result_t _ze_admin_space_build_ke(z_owned_keyexpr_t *ke, const z_id_t *zid, const _ze_admin_space_ke_segment_t *segments, size_t segment_count) { z_internal_keyexpr_null(ke); z_owned_string_t zid_str; _Z_RETURN_IF_ERR(z_id_to_string(zid, &zid_str)); const z_loaned_string_t *zid_loan = z_string_loan(&zid_str); const char *zid_data = z_string_data(zid_loan); size_t zid_len = z_string_len(zid_loan); char buf[_ZE_ADMIN_SPACE_KE_BUF_LEN]; size_t off = 0; int n = snprintf(buf + off, sizeof(buf) - off, "%s%s%.*s", _Z_KEYEXPR_AT, _Z_KEYEXPR_SEPARATOR, (int)zid_len, zid_data); z_string_drop(z_string_move(&zid_str)); if (n < 0 || (size_t)n >= sizeof(buf) - off) { return _Z_ERR_INVALID; } off += (size_t)n; for (size_t i = 0; i < segment_count; i++) { n = snprintf(buf + off, sizeof(buf) - off, "%s%.*s", _Z_KEYEXPR_SEPARATOR, (int)segments[i].len, segments[i].data); if (n < 0 || (size_t)n >= sizeof(buf) - off) { return _Z_ERR_INVALID; } off += (size_t)n; } return z_keyexpr_from_substr(ke, buf, off); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / Z_KEYEXPR_STARSTAR static z_result_t _ze_admin_space_pico_queryable_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_STARSTAR, _Z_KEYEXPR_STARSTAR_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 2); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO static z_result_t _ze_admin_space_pico_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 1); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / _Z_KEYEXPR_SESSION static z_result_t _ze_admin_space_pico_session_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 2); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORTS static z_result_t _ze_admin_space_pico_transports_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORTS, _Z_KEYEXPR_TRANSPORTS_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 3); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORTS / 0 static z_result_t _ze_admin_space_pico_transports_0_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORTS, _Z_KEYEXPR_TRANSPORTS_LEN}, {"0", 1}, }; return _ze_admin_space_build_ke(ke, zid, segments, 4); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORTS / 0 / _Z_KEYEXPR_PEERS static z_result_t _ze_admin_space_pico_transports_0_peers_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORTS, _Z_KEYEXPR_TRANSPORTS_LEN}, {"0", 1}, {_Z_KEYEXPR_PEERS, _Z_KEYEXPR_PEERS_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 5); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_PICO / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORTS / 0 / _Z_KEYEXPR_PEERS / // REMOTE_ZID static z_result_t _ze_admin_space_pico_transports_0_peers_peer_ke(z_owned_keyexpr_t *ke, const z_id_t *zid, const z_id_t *remote_zid) { z_owned_string_t remote_zid_str; _Z_RETURN_IF_ERR(z_id_to_string(remote_zid, &remote_zid_str)); const z_loaned_string_t *remote_zid_loan = z_string_loan(&remote_zid_str); const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_PICO, _Z_KEYEXPR_PICO_LEN}, {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORTS, _Z_KEYEXPR_TRANSPORTS_LEN}, {"0", 1}, {_Z_KEYEXPR_PEERS, _Z_KEYEXPR_PEERS_LEN}, {z_string_data(remote_zid_loan), z_string_len(remote_zid_loan)}, }; z_result_t ret = _ze_admin_space_build_ke(ke, zid, segments, 6); z_string_drop(z_string_move(&remote_zid_str)); return ret; } #if Z_FEATURE_CONNECTIVITY == 1 // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_SESSION / Z_KEYEXPR_STARSTAR static z_result_t _ze_admin_space_session_queryable_ke(z_owned_keyexpr_t *ke, const z_id_t *zid) { static const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_STARSTAR, _Z_KEYEXPR_STARSTAR_LEN}, }; return _ze_admin_space_build_ke(ke, zid, segments, 2); } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORT_UNICAST / PEER_ZID static z_result_t _ze_admin_space_session_transport_ke(z_owned_keyexpr_t *ke, const z_id_t *zid, const z_id_t *peer_zid) { z_owned_string_t peer_zid_str; _Z_RETURN_IF_ERR(z_id_to_string(peer_zid, &peer_zid_str)); const z_loaned_string_t *peer_zid_loan = z_string_loan(&peer_zid_str); const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORT_UNICAST, _Z_KEYEXPR_TRANSPORT_UNICAST_LEN}, {z_string_data(peer_zid_loan), z_string_len(peer_zid_loan)}, }; z_result_t ret = _ze_admin_space_build_ke(ke, zid, segments, 3); z_string_drop(z_string_move(&peer_zid_str)); return ret; } // ke = _Z_KEYEXPR_AT / ZID / _Z_KEYEXPR_SESSION / _Z_KEYEXPR_TRANSPORT_UNICAST / PEER_ZID / _Z_KEYEXPR_LINK / // LINK_ID static z_result_t _ze_admin_space_session_link_ke(z_owned_keyexpr_t *ke, const z_id_t *zid, const z_id_t *peer_zid, const z_id_t *link_id) { z_owned_string_t peer_zid_str; _Z_RETURN_IF_ERR(z_id_to_string(peer_zid, &peer_zid_str)); z_owned_string_t link_id_str; _Z_CLEAN_RETURN_IF_ERR(z_id_to_string(link_id, &link_id_str), z_string_drop(z_string_move(&peer_zid_str))); const z_loaned_string_t *peer_zid_loan = z_string_loan(&peer_zid_str); const z_loaned_string_t *link_id_loan = z_string_loan(&link_id_str); const _ze_admin_space_ke_segment_t segments[] = { {_Z_KEYEXPR_SESSION, _Z_KEYEXPR_SESSION_LEN}, {_Z_KEYEXPR_TRANSPORT_UNICAST, _Z_KEYEXPR_TRANSPORT_UNICAST_LEN}, {z_string_data(peer_zid_loan), z_string_len(peer_zid_loan)}, {_Z_KEYEXPR_LINK, _Z_KEYEXPR_LINK_LEN}, {z_string_data(link_id_loan), z_string_len(link_id_loan)}, }; z_result_t ret = _ze_admin_space_build_ke(ke, zid, segments, 5); z_string_drop(z_string_move(&peer_zid_str)); z_string_drop(z_string_move(&link_id_str)); return ret; } #endif // Z_FEATURE_CONNECTIVITY == 1 static z_result_t _ze_admin_space_encode_whatami(_z_json_encoder_t *je, z_whatami_t mode) { switch (mode) { case Z_WHATAMI_ROUTER: return _z_json_encoder_write_string(je, "router"); case Z_WHATAMI_PEER: return _z_json_encoder_write_string(je, "peer"); case Z_WHATAMI_CLIENT: return _z_json_encoder_write_string(je, "client"); default: return _Z_ERR_INVALID; } } #if Z_FEATURE_CONNECTIVITY == 1 static z_result_t _ze_admin_space_encode_connectivity_transport_payload(z_owned_bytes_t *payload, const z_id_t *zid, z_whatami_t whatami, bool is_qos, bool is_shm) { z_internal_bytes_null(payload); _z_json_encoder_t je; _Z_RETURN_IF_ERR(_z_json_encoder_empty(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_start_object(&je), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "zid"), _z_json_encoder_clear(&je)); z_owned_string_t zid_str; _Z_CLEAN_RETURN_IF_ERR(z_id_to_string(zid, &zid_str), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_z_string(&je, z_string_loan(&zid_str)), z_string_drop(z_string_move(&zid_str)); _z_json_encoder_clear(&je)); z_string_drop(z_string_move(&zid_str)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "whatami"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_ze_admin_space_encode_whatami(&je, whatami), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "is_qos"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_boolean(&je, is_qos), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "is_shm"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_boolean(&je, is_shm), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_end_object(&je), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_finish(&je, payload), _z_json_encoder_clear(&je)); return _Z_RES_OK; } static z_result_t _ze_admin_space_encode_connectivity_link_payload(z_owned_bytes_t *payload, const _z_string_t *src, const _z_string_t *dst, uint16_t mtu, bool is_streamed, bool is_reliable) { z_internal_bytes_null(payload); _z_json_encoder_t je; _Z_RETURN_IF_ERR(_z_json_encoder_empty(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_start_object(&je), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "src"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_z_string(&je, src), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "dst"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_z_string(&je, dst), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "group"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_null(&je), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "mtu"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_u64(&je, (uint64_t)mtu), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "is_reliable"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_boolean(&je, is_reliable), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(&je, "is_streamed"), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_boolean(&je, is_streamed), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_end_object(&je), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_finish(&je, payload), _z_json_encoder_clear(&je)); return _Z_RES_OK; } #endif // Z_FEATURE_CONNECTIVITY == 1 static z_result_t _ze_admin_space_encode_transport_type(_z_json_encoder_t *je, int type) { switch (type) { case _Z_TRANSPORT_UNICAST_TYPE: return _z_json_encoder_write_string(je, "unicast"); case _Z_TRANSPORT_MULTICAST_TYPE: return _z_json_encoder_write_string(je, "multicast"); case _Z_TRANSPORT_RAWETH_TYPE: return _z_json_encoder_write_string(je, "raweth"); default: return _Z_ERR_INVALID; } } static z_result_t _ze_admin_space_encode_str_intmap(_z_json_encoder_t *je, const _z_str_intmap_t *map) { _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _z_str_intmap_iterator_t it = _z_str_intmap_iterator_make(map); while (_z_str_intmap_iterator_next(&it)) { size_t key = _z_str_intmap_iterator_key(&it); char key_buf[21]; // enough for 64-bit size_t int n = snprintf(key_buf, sizeof(key_buf), "%zu", key); if (n <= 0 || (size_t)n >= sizeof(key_buf)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, key_buf)); char *value = _z_str_intmap_iterator_value(&it); _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, value)); } return _z_json_encoder_end_object(je); } static z_result_t _ze_admin_space_encode_link(_z_json_encoder_t *je, const _z_link_t *link) { _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "link")); _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "type")); switch (link->_type) { case _Z_LINK_TYPE_TCP: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "tcp")); break; case _Z_LINK_TYPE_UDP: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "udp")); break; case _Z_LINK_TYPE_BT: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "bt")); break; case _Z_LINK_TYPE_SERIAL: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "serial")); break; case _Z_LINK_TYPE_WS: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "ws")); break; case _Z_LINK_TYPE_TLS: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "tls")); break; case _Z_LINK_TYPE_RAWETH: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "raweth")); break; default: return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "endpoint")); _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "locator")); _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "metadata")); _Z_RETURN_IF_ERR(_ze_admin_space_encode_str_intmap(je, &link->_endpoint._locator._metadata)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "protocol")); _Z_RETURN_IF_ERR(_z_json_encoder_write_z_string(je, &link->_endpoint._locator._protocol)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "address")); _Z_RETURN_IF_ERR(_z_json_encoder_write_z_string(je, &link->_endpoint._locator._address)); _Z_RETURN_IF_ERR(_z_json_encoder_end_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "config")); _Z_RETURN_IF_ERR(_ze_admin_space_encode_str_intmap(je, &link->_endpoint._config)); _Z_RETURN_IF_ERR(_z_json_encoder_end_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "capabilities")); _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "transport")); switch (link->_cap._transport) { case Z_LINK_CAP_TRANSPORT_UNICAST: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "unicast")); break; case Z_LINK_CAP_TRANSPORT_MULTICAST: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "multicast")); break; case Z_LINK_CAP_TRANSPORT_RAWETH: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "raweth")); break; default: return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "flow")); switch (link->_cap._flow) { case Z_LINK_CAP_FLOW_DATAGRAM: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "datagram")); break; case Z_LINK_CAP_FLOW_STREAM: _Z_RETURN_IF_ERR(_z_json_encoder_write_string(je, "stream")); break; default: return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "is_reliable")); _Z_RETURN_IF_ERR(_z_json_encoder_write_boolean(je, link->_cap._is_reliable != 0)); _Z_RETURN_IF_ERR(_z_json_encoder_end_object(je)); return _z_json_encoder_end_object(je); } static void _ze_admin_space_reply_null(_ze_admin_space_reply_t *reply) { z_internal_keyexpr_null(&reply->ke); z_internal_bytes_null(&reply->payload); } void _ze_admin_space_reply_clear(_ze_admin_space_reply_t *reply) { z_keyexpr_drop(z_keyexpr_move(&reply->ke)); z_bytes_drop(z_bytes_move(&reply->payload)); _ze_admin_space_reply_null(reply); } static z_result_t _ze_admin_space_add_reply_bytes(const z_loaned_keyexpr_t *ke, z_moved_bytes_t *payload, _ze_admin_space_reply_list_t **replies) { _ze_admin_space_reply_t *reply = z_malloc(sizeof(_ze_admin_space_reply_t)); if (reply == NULL) { z_bytes_drop(payload); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _ze_admin_space_reply_null(reply); _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&reply->ke, ke), z_bytes_drop(payload); z_free(reply)); z_bytes_take(&reply->payload, payload); _ze_admin_space_reply_list_t *old = *replies; _ze_admin_space_reply_list_t *tmp = _ze_admin_space_reply_list_push(*replies, reply); if (tmp == old) { _ze_admin_space_reply_clear(reply); z_free(reply); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } *replies = tmp; return _Z_RES_OK; } static z_result_t _ze_admin_space_add_reply(_z_json_encoder_t *je, const z_loaned_keyexpr_t *ke, _ze_admin_space_reply_list_t **replies) { z_owned_bytes_t payload; z_internal_bytes_null(&payload); _Z_RETURN_IF_ERR(_z_json_encoder_finish(je, &payload)); return _ze_admin_space_add_reply_bytes(ke, z_bytes_move(&payload), replies); } static z_result_t _ze_admin_space_encode_transport_common(_z_json_encoder_t *je, const _z_transport_common_t *common) { return _ze_admin_space_encode_link(je, common->_link); } static z_result_t _ze_admin_space_encode_peer_common(_z_json_encoder_t *je, const _z_transport_peer_common_t *peer) { _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "zid")); z_owned_string_t id_string; _Z_RETURN_IF_ERR(z_id_to_string(&peer->_remote_zid, &id_string)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_z_string(je, z_string_loan(&id_string)), z_string_drop(z_string_move(&id_string))); z_string_drop(z_string_move(&id_string)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "whatami")); return _ze_admin_space_encode_whatami(je, peer->_remote_whatami); } static z_result_t _ze_admin_space_encode_unicast_peer(_z_json_encoder_t *je, const _z_transport_peer_unicast_t *peer) { _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_ze_admin_space_encode_peer_common(je, &peer->common)); return _z_json_encoder_end_object(je); } static z_result_t _ze_admin_space_encode_multicast_peer(_z_json_encoder_t *je, const _z_transport_peer_multicast_t *peer) { _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_ze_admin_space_encode_peer_common(je, &peer->common)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "remote_addr")); _Z_RETURN_IF_ERR(_z_json_encoder_write_z_slice(je, &peer->_remote_addr)); return _z_json_encoder_end_object(je); } static z_result_t _ze_admin_space_encode_unicast_peers(_z_json_encoder_t *je, const _z_transport_peer_unicast_slist_t *peers) { _Z_RETURN_IF_ERR(_z_json_encoder_start_array(je)); for (; peers != NULL; peers = _z_transport_peer_unicast_slist_next(peers)) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(peers); _Z_RETURN_IF_ERR(_ze_admin_space_encode_unicast_peer(je, peer)); } return _z_json_encoder_end_array(je); } static z_result_t _ze_admin_space_encode_multicast_peers(_z_json_encoder_t *je, const _z_transport_peer_multicast_slist_t *peers) { _Z_RETURN_IF_ERR(_z_json_encoder_start_array(je)); for (; peers != NULL; peers = _z_transport_peer_multicast_slist_next(peers)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(peers); _Z_RETURN_IF_ERR(_ze_admin_space_encode_multicast_peer(je, peer)); } return _z_json_encoder_end_array(je); } static z_result_t _ze_admin_space_encode_transport_unicast(_z_json_encoder_t *je, _z_transport_unicast_t *tp) { z_result_t ret = _Z_RES_OK; _z_transport_peer_mutex_lock(&tp->_common); ret = _ze_admin_space_encode_transport_common(je, &tp->_common); if (ret == _Z_RES_OK) { ret = _z_json_encoder_write_key(je, "peers"); } if (ret == _Z_RES_OK) { ret = _ze_admin_space_encode_unicast_peers(je, tp->_peers); } _z_transport_peer_mutex_unlock(&tp->_common); return ret; } static z_result_t _ze_admin_space_encode_transport_multicast(_z_json_encoder_t *je, _z_transport_multicast_t *tp) { z_result_t ret = _Z_RES_OK; _z_transport_peer_mutex_lock(&tp->_common); ret = _ze_admin_space_encode_transport_common(je, &tp->_common); if (ret == _Z_RES_OK) { ret = _z_json_encoder_write_key(je, "peers"); } if (ret == _Z_RES_OK) { ret = _ze_admin_space_encode_multicast_peers(je, tp->_peers); } _z_transport_peer_mutex_unlock(&tp->_common); return ret; } static z_result_t _ze_admin_space_encode_transport(_z_json_encoder_t *je, _z_transport_t *tp) { _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "type")); _Z_RETURN_IF_ERR(_ze_admin_space_encode_transport_type(je, tp->_type)); switch (tp->_type) { case _Z_TRANSPORT_UNICAST_TYPE: _Z_RETURN_IF_ERR(_ze_admin_space_encode_transport_unicast(je, &tp->_transport._unicast)); break; case _Z_TRANSPORT_MULTICAST_TYPE: _Z_RETURN_IF_ERR(_ze_admin_space_encode_transport_multicast(je, &tp->_transport._multicast)); break; case _Z_TRANSPORT_RAWETH_TYPE: _Z_RETURN_IF_ERR(_ze_admin_space_encode_transport_multicast(je, &tp->_transport._raweth)); break; default: break; } return _z_json_encoder_end_object(je); } static z_result_t _ze_admin_space_encode_transport_peers(_z_json_encoder_t *je, _z_transport_t *tp) { z_result_t ret = _Z_RES_OK; switch (tp->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_unicast_t *utp = &tp->_transport._unicast; _z_transport_peer_mutex_lock(&utp->_common); ret = _ze_admin_space_encode_unicast_peers(je, utp->_peers); _z_transport_peer_mutex_unlock(&utp->_common); break; } case _Z_TRANSPORT_MULTICAST_TYPE: { _z_transport_multicast_t *mtp = &tp->_transport._multicast; _z_transport_peer_mutex_lock(&mtp->_common); ret = _ze_admin_space_encode_multicast_peers(je, mtp->_peers); _z_transport_peer_mutex_unlock(&mtp->_common); break; } case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_multicast_t *rtp = &tp->_transport._raweth; _z_transport_peer_mutex_lock(&rtp->_common); ret = _ze_admin_space_encode_multicast_peers(je, rtp->_peers); _z_transport_peer_mutex_unlock(&rtp->_common); break; } default: break; } return ret; } static z_result_t _ze_admin_space_encode_transports(_z_json_encoder_t *je, _z_transport_t *tp) { _Z_RETURN_IF_ERR(_z_json_encoder_start_array(je)); _Z_RETURN_IF_ERR(_ze_admin_space_encode_transport(je, tp)); return _z_json_encoder_end_array(je); } static z_result_t _ze_admin_space_encode_session(_z_json_encoder_t *je, _z_session_t *session) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(session)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_start_object(je), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(je, "zid"), _z_session_mutex_unlock(session)); z_owned_string_t zid_str; _Z_CLEAN_RETURN_IF_ERR(z_id_to_string(&session->_local_zid, &zid_str), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_z_string(je, z_string_loan(&zid_str)), z_string_drop(z_string_move(&zid_str)); _z_session_mutex_unlock(session)); z_string_drop(z_string_move(&zid_str)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(je, "whatami"), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_ze_admin_space_encode_whatami(je, session->_mode), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_write_key(je, "transports"), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_ze_admin_space_encode_transports(je, &session->_tp), _z_session_mutex_unlock(session)); _Z_CLEAN_RETURN_IF_ERR(_z_json_encoder_end_object(je), _z_session_mutex_unlock(session)); _z_session_mutex_unlock(session); return _Z_RES_OK; } static z_result_t _ze_admin_space_encode_pico(_z_json_encoder_t *je, void *ctx) { _z_session_t *session = (_z_session_t *)ctx; _Z_RETURN_IF_ERR(_z_json_encoder_start_object(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_key(je, "session")); _Z_RETURN_IF_ERR(_ze_admin_space_encode_session(je, session)); return _z_json_encoder_end_object(je); } static z_result_t _ze_admin_space_encode_pico_session(_z_json_encoder_t *je, void *ctx) { _z_session_t *session = (_z_session_t *)ctx; return _ze_admin_space_encode_session(je, session); } static z_result_t _ze_admin_space_encode_pico_transports(_z_json_encoder_t *je, void *ctx) { _z_session_t *session = (_z_session_t *)ctx; _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(session)); z_result_t ret = _ze_admin_space_encode_transports(je, &session->_tp); _z_session_mutex_unlock(session); return ret; } static z_result_t _ze_admin_space_encode_pico_transport_0(_z_json_encoder_t *je, void *ctx) { _z_session_t *session = (_z_session_t *)ctx; _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(session)); z_result_t ret = _ze_admin_space_encode_transport(je, &session->_tp); _z_session_mutex_unlock(session); return ret; } static z_result_t _ze_admin_space_encode_pico_transport_0_peers(_z_json_encoder_t *je, void *ctx) { _z_session_t *session = (_z_session_t *)ctx; _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(session)); z_result_t ret = _ze_admin_space_encode_transport_peers(je, &session->_tp); _z_session_mutex_unlock(session); return ret; } static z_result_t _ze_admin_space_reply_if_intersects(const z_loaned_query_t *query, const z_loaned_keyexpr_t *ke, void *ctx, _ze_admin_space_reply_list_t **replies, _ze_admin_space_encode_fn_t encode) { if (z_keyexpr_intersects(z_query_keyexpr(query), ke)) { _z_json_encoder_t je; _Z_RETURN_IF_ERR(_z_json_encoder_empty(&je)); _Z_CLEAN_RETURN_IF_ERR(encode(&je, ctx), _z_json_encoder_clear(&je)); _Z_CLEAN_RETURN_IF_ERR(_ze_admin_space_add_reply(&je, ke, replies), _z_json_encoder_clear(&je)); } return _Z_RES_OK; } typedef struct { const _z_transport_peer_unicast_t *peer; } _ze_admin_space_unicast_peer_ctx_t; typedef struct { const _z_transport_peer_multicast_t *peer; } _ze_admin_space_multicast_peer_ctx_t; static z_result_t _ze_admin_space_encode_pico_transport_0_unicast_peer(_z_json_encoder_t *je, void *ctx) { const _ze_admin_space_unicast_peer_ctx_t *pctx = (const _ze_admin_space_unicast_peer_ctx_t *)ctx; return _ze_admin_space_encode_unicast_peer(je, pctx->peer); } static z_result_t _ze_admin_space_encode_pico_transport_0_multicast_peer(_z_json_encoder_t *je, void *ctx) { const _ze_admin_space_multicast_peer_ctx_t *pctx = (const _ze_admin_space_multicast_peer_ctx_t *)ctx; return _ze_admin_space_encode_multicast_peer(je, pctx->peer); } static void _ze_admin_space_query_handle_pico_transport_0_unicast_peers(const z_loaned_query_t *query, const z_id_t *local_zid, _z_transport_unicast_t *tp, _ze_admin_space_reply_list_t **replies) { _z_transport_peer_mutex_lock(&tp->_common); for (_z_transport_peer_unicast_slist_t *peers = tp->_peers; peers != NULL; peers = _z_transport_peer_unicast_slist_next(peers)) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(peers); z_owned_keyexpr_t ke; z_result_t ret; ret = _ze_admin_space_pico_transports_0_peers_peer_ke(&ke, local_zid, &peer->common._remote_zid); if (ret != _Z_RES_OK) { _Z_WARN("Failed to build key expression for pico/session/transports/0/peers peer query: %d", ret); continue; } _ze_admin_space_unicast_peer_ctx_t ctx = { .peer = peer, }; ret = _ze_admin_space_reply_if_intersects(query, z_keyexpr_loan(&ke), &ctx, replies, _ze_admin_space_encode_pico_transport_0_unicast_peer); if (ret != _Z_RES_OK) { _Z_WARN("Failed to handle admin space query for pico/session/transports/0/peers peer endpoint: %d", ret); } z_keyexpr_drop(z_keyexpr_move(&ke)); } _z_transport_peer_mutex_unlock(&tp->_common); } static void _ze_admin_space_query_handle_pico_transport_0_multicast_peers(const z_loaned_query_t *query, const z_id_t *local_zid, _z_transport_multicast_t *tp, _ze_admin_space_reply_list_t **replies) { _z_transport_peer_mutex_lock(&tp->_common); for (_z_transport_peer_multicast_slist_t *peers = tp->_peers; peers != NULL; peers = _z_transport_peer_multicast_slist_next(peers)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(peers); z_owned_keyexpr_t ke; z_result_t ret; ret = _ze_admin_space_pico_transports_0_peers_peer_ke(&ke, local_zid, &peer->common._remote_zid); if (ret != _Z_RES_OK) { _Z_WARN("Failed to build key expression for pico/session/transports/0/peers peer query: %d", ret); continue; } _ze_admin_space_multicast_peer_ctx_t ctx = { .peer = peer, }; ret = _ze_admin_space_reply_if_intersects(query, z_keyexpr_loan(&ke), &ctx, replies, _ze_admin_space_encode_pico_transport_0_multicast_peer); if (ret != _Z_RES_OK) { _Z_WARN("Failed to handle admin space query for pico/session/transports/0/peers peer endpoint: %d", ret); } z_keyexpr_drop(z_keyexpr_move(&ke)); } _z_transport_peer_mutex_unlock(&tp->_common); } static void _ze_admin_space_query_handle_pico_transport_0_peers(const z_loaned_query_t *query, _z_session_t *session, _ze_admin_space_reply_list_t **replies) { switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: _ze_admin_space_query_handle_pico_transport_0_unicast_peers(query, &session->_local_zid, &session->_tp._transport._unicast, replies); break; case _Z_TRANSPORT_MULTICAST_TYPE: _ze_admin_space_query_handle_pico_transport_0_multicast_peers(query, &session->_local_zid, &session->_tp._transport._multicast, replies); break; case _Z_TRANSPORT_RAWETH_TYPE: _ze_admin_space_query_handle_pico_transport_0_multicast_peers(query, &session->_local_zid, &session->_tp._transport._raweth, replies); break; default: break; } } static void _ze_admin_space_try_reply(const z_loaned_query_t *query, _z_session_t *session, _ze_admin_space_reply_list_t **replies, const _ze_admin_space_endpoint_t *ep) { z_owned_keyexpr_t ke; z_result_t ret = ep->build_ke(&ke, &session->_local_zid); if (ret != _Z_RES_OK) { _Z_WARN("Failed to build key expression for %s query: %d", ep->name, ret); return; } ret = _ze_admin_space_reply_if_intersects(query, z_keyexpr_loan(&ke), session, replies, ep->encode); if (ret != _Z_RES_OK) { _Z_WARN("Failed to handle admin space query for %s endpoint: %d", ep->name, ret); } z_keyexpr_drop(z_keyexpr_move(&ke)); } static void _ze_admin_space_query_handle_endpoints(const z_loaned_query_t *query, _z_session_t *session, const _ze_admin_space_endpoint_t *endpoints, size_t endpoint_count, _ze_admin_space_reply_list_t **replies) { for (size_t i = 0; i < endpoint_count; i++) { _ze_admin_space_try_reply(query, session, replies, &endpoints[i]); } } static const _ze_admin_space_endpoint_t _ze_admin_space_pico_endpoints[] = { {"pico", _ze_admin_space_pico_ke, _ze_admin_space_encode_pico}, {"pico/session", _ze_admin_space_pico_session_ke, _ze_admin_space_encode_pico_session}, {"pico/session/transports", _ze_admin_space_pico_transports_ke, _ze_admin_space_encode_pico_transports}, {"pico/session/transports/0", _ze_admin_space_pico_transports_0_ke, _ze_admin_space_encode_pico_transport_0}, {"pico/session/transports/0/peers", _ze_admin_space_pico_transports_0_peers_ke, _ze_admin_space_encode_pico_transport_0_peers}, }; static void _ze_admin_space_query_handle_pico(const z_loaned_query_t *query, _z_session_t *session, _ze_admin_space_reply_list_t **replies) { _ze_admin_space_query_handle_endpoints(query, session, _ze_admin_space_pico_endpoints, _ZP_ARRAY_SIZE(_ze_admin_space_pico_endpoints), replies); _ze_admin_space_query_handle_pico_transport_0_peers(query, session, replies); } #if Z_FEATURE_CONNECTIVITY == 1 static void _ze_admin_space_query_handle_connectivity_session(const z_loaned_query_t *query, _z_session_t *session, _ze_admin_space_reply_list_t **replies) { _z_session_transport_mutex_lock(session); if (session->_tp._type != _Z_TRANSPORT_UNICAST_TYPE) { _z_session_transport_mutex_unlock(session); return; } _z_transport_peer_mutex_lock(&session->_tp._transport._unicast._common); const _z_transport_common_t *transport_common = &session->_tp._transport._unicast._common; const _z_transport_peer_unicast_slist_t *peers = session->_tp._transport._unicast._peers; while (peers != NULL) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(peers); z_owned_keyexpr_t transport_ke; z_result_t ret = _ze_admin_space_session_transport_ke(&transport_ke, &session->_local_zid, &peer->common._remote_zid); if (ret == _Z_RES_OK) { if (z_keyexpr_intersects(z_query_keyexpr(query), z_keyexpr_loan(&transport_ke))) { z_owned_bytes_t payload; ret = _ze_admin_space_encode_connectivity_transport_payload(&payload, &peer->common._remote_zid, peer->common._remote_whatami, false, false); if (ret == _Z_RES_OK) { ret = _ze_admin_space_add_reply_bytes(z_keyexpr_loan(&transport_ke), z_bytes_move(&payload), replies); } if (ret != _Z_RES_OK) { _Z_WARN("Failed to add connectivity transport status reply: %d", ret); } } z_keyexpr_drop(z_keyexpr_move(&transport_ke)); } else { _Z_WARN("Failed to build key expression for connectivity transport status: %d", ret); } z_owned_keyexpr_t link_ke; ret = _ze_admin_space_session_link_ke(&link_ke, &session->_local_zid, &peer->common._remote_zid, &peer->common._remote_zid); if (ret == _Z_RES_OK) { if (z_keyexpr_intersects(z_query_keyexpr(query), z_keyexpr_loan(&link_ke))) { uint16_t mtu; bool is_streamed; bool is_reliable; _z_transport_link_properties_from_transport(transport_common, &mtu, &is_streamed, &is_reliable); z_owned_bytes_t payload; z_internal_bytes_null(&payload); ret = _ze_admin_space_encode_connectivity_link_payload( &payload, &peer->common._link_src, &peer->common._link_dst, mtu, is_streamed, is_reliable); if (ret == _Z_RES_OK) { ret = _ze_admin_space_add_reply_bytes(z_keyexpr_loan(&link_ke), z_bytes_move(&payload), replies); } if (ret != _Z_RES_OK) { _Z_WARN("Failed to add connectivity link status reply: %d", ret); } } z_keyexpr_drop(z_keyexpr_move(&link_ke)); } else { _Z_WARN("Failed to build key expression for connectivity link status: %d", ret); } peers = _z_transport_peer_unicast_slist_next(peers); } _z_transport_peer_mutex_unlock(&session->_tp._transport._unicast._common); _z_session_transport_mutex_unlock(session); } #endif // Z_FEATURE_CONNECTIVITY == 1 static void _ze_admin_space_query_reply_all(z_loaned_query_t *query, _ze_admin_space_reply_list_t **replies) { _ze_admin_space_reply_list_t *next = *replies; while (next != NULL) { _ze_admin_space_reply_t *reply = _ze_admin_space_reply_list_value(next); z_query_reply_options_t opt; z_query_reply_options_default(&opt); z_owned_encoding_t encoding; if (z_encoding_clone(&encoding, z_encoding_application_json()) == _Z_RES_OK) { opt.encoding = z_encoding_move(&encoding); z_result_t res = z_query_reply(query, z_keyexpr_loan(&reply->ke), z_bytes_move(&reply->payload), &opt); if (res != _Z_RES_OK) { z_view_string_t keystr; if (z_keyexpr_as_view_string(z_keyexpr_loan(&reply->ke), &keystr) == _Z_RES_OK) { _Z_ERROR("Failed to reply to admin space query on key expression: %.*s", (int)z_string_len(z_view_string_loan(&keystr)), z_string_data(z_view_string_loan(&keystr))); } else { _Z_ERROR("Failed to reply to admin space query"); } } } else { _Z_ERROR("Failed to clone JSON encoding for admin space query reply"); break; } next = _ze_admin_space_reply_list_next(next); } } static void _ze_admin_space_query_handler_impl(z_loaned_query_t *query, void *ctx, bool include_pico, bool include_connectivity_session) { _z_session_weak_t *session_weak = (_z_session_weak_t *)ctx; _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(session_weak); if (_Z_RC_IS_NULL(&session_rc)) { _Z_ERROR("Dropped admin space query - session closed"); return; } _z_session_t *session = _Z_RC_IN_VAL(&session_rc); _ze_admin_space_reply_list_t *replies = _ze_admin_space_reply_list_new(); if (include_pico) { _ze_admin_space_query_handle_pico(query, session, &replies); } #if Z_FEATURE_CONNECTIVITY == 1 if (include_connectivity_session) { _ze_admin_space_query_handle_connectivity_session(query, session, &replies); } #else _ZP_UNUSED(include_connectivity_session); #endif _ze_admin_space_query_reply_all(query, &replies); _ze_admin_space_reply_list_free(&replies); _z_session_rc_drop(&session_rc); } static void _ze_admin_space_pico_query_handler(z_loaned_query_t *query, void *ctx) { _ze_admin_space_query_handler_impl(query, ctx, true, false); } #if Z_FEATURE_CONNECTIVITY == 1 static void _ze_admin_space_session_query_handler(z_loaned_query_t *query, void *ctx) { _ze_admin_space_query_handler_impl(query, ctx, false, true); } #endif static void _ze_admin_space_query_dropper(void *ctx) { _z_session_weak_t *session_weak = (_z_session_weak_t *)ctx; _z_session_weak_drop(session_weak); z_free(session_weak); } static z_result_t _ze_admin_space_undeclare_queryable(const z_loaned_session_t *zs, uint32_t queryable_id) { if (queryable_id == 0) { return _Z_RES_OK; } _z_queryable_t queryable = { ._entity_id = queryable_id, ._zn = _z_session_rc_clone_as_weak(zs), }; if (_Z_RC_IS_NULL(&queryable._zn)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } z_result_t ret = _z_undeclare_queryable(&queryable); _z_queryable_clear(&queryable); return ret; } #if Z_FEATURE_CONNECTIVITY == 1 static z_result_t _ze_admin_space_declare_session_queryable(const z_loaned_session_t *zs, uint32_t *out_queryable_id, const z_loaned_keyexpr_t *ke, void *ctx) { z_owned_closure_query_t callback; z_result_t ret = z_closure_query(&callback, _ze_admin_space_session_query_handler, _ze_admin_space_query_dropper, ctx); if (ret != _Z_RES_OK) { _ze_admin_space_query_dropper(ctx); return ret; } _z_closure_query_t closure = callback._val; z_internal_closure_query_null(&callback); return _z_register_queryable(out_queryable_id, zs, ke, _Z_QUERYABLE_COMPLETE_DEFAULT, closure.call, closure.drop, closure.context, Z_LOCALITY_SESSION_LOCAL, NULL); } #endif #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_PUBLICATION == 1 typedef struct { _z_session_weak_t _session; } _ze_admin_space_connectivity_listener_ctx_t; static void _ze_admin_space_connectivity_listener_ctx_drop(void *ctx) { _ze_admin_space_connectivity_listener_ctx_t *listener_ctx = (_ze_admin_space_connectivity_listener_ctx_t *)ctx; if (listener_ctx != NULL) { _z_session_weak_drop(&listener_ctx->_session); z_free(listener_ctx); } } static _ze_admin_space_connectivity_listener_ctx_t *_ze_admin_space_connectivity_listener_ctx_new( const z_loaned_session_t *zs) { _ze_admin_space_connectivity_listener_ctx_t *ctx = (_ze_admin_space_connectivity_listener_ctx_t *)z_malloc(sizeof(_ze_admin_space_connectivity_listener_ctx_t)); if (ctx == NULL) { return NULL; } ctx->_session = _z_session_rc_clone_as_weak(zs); if (_Z_RC_IS_NULL(&ctx->_session)) { z_free(ctx); return NULL; } return ctx; } static z_result_t _ze_admin_space_undeclare_transport_listener(z_loaned_session_t *zs, size_t listener_id) { if (listener_id == 0) { return _Z_RES_OK; } z_owned_transport_events_listener_t listener; listener._val = (_z_transport_events_listener_t){0}; listener._val._id = listener_id; listener._val._session = _z_session_rc_clone_as_weak(zs); if (_Z_RC_IS_NULL(&listener._val._session)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return z_undeclare_transport_events_listener(z_transport_events_listener_move(&listener)); } static z_result_t _ze_admin_space_undeclare_link_listener(z_loaned_session_t *zs, size_t listener_id) { if (listener_id == 0) { return _Z_RES_OK; } z_owned_link_events_listener_t listener; listener._val = (_z_link_events_listener_t){0}; listener._val._id = listener_id; listener._val._session = _z_session_rc_clone_as_weak(zs); if (_Z_RC_IS_NULL(&listener._val._session)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return z_undeclare_link_events_listener(z_link_events_listener_move(&listener)); } static inline void _ze_admin_space_transport_listener_handle_clear(z_owned_transport_events_listener_t *listener) { _z_session_weak_drop(&listener->_val._session); _z_sync_group_drop(&listener->_val._callback_drop_sync_group); listener->_val = (_z_transport_events_listener_t){0}; } static inline void _ze_admin_space_link_listener_handle_clear(z_owned_link_events_listener_t *listener) { _z_session_weak_drop(&listener->_val._session); _z_sync_group_drop(&listener->_val._callback_drop_sync_group); listener->_val = (_z_link_events_listener_t){0}; } static z_result_t _ze_admin_space_put_session_local_json(_z_session_t *session, const z_loaned_keyexpr_t *ke, z_owned_bytes_t *payload) { z_owned_encoding_t encoding; z_result_t ret = z_encoding_clone(&encoding, z_encoding_application_json()); if (ret != _Z_RES_OK) { z_bytes_drop(z_bytes_move(payload)); return ret; } ret = _z_write(session, ke, &payload->_val, &encoding._val, Z_SAMPLE_KIND_PUT, z_internal_congestion_control_default_push(), Z_PRIORITY_DEFAULT, false, NULL, NULL, Z_RELIABILITY_DEFAULT, NULL, Z_LOCALITY_SESSION_LOCAL); z_encoding_drop(z_encoding_move(&encoding)); z_bytes_drop(z_bytes_move(payload)); return ret; } static z_result_t _ze_admin_space_delete_session_local(_z_session_t *session, const z_loaned_keyexpr_t *ke) { return _z_write(session, ke, NULL, NULL, Z_SAMPLE_KIND_DELETE, z_internal_congestion_control_default_push(), Z_PRIORITY_DEFAULT, false, NULL, NULL, Z_RELIABILITY_DEFAULT, NULL, Z_LOCALITY_SESSION_LOCAL); } static void _ze_admin_space_publish_transport_event(z_loaned_transport_event_t *event, void *ctx) { _ze_admin_space_connectivity_listener_ctx_t *listener_ctx = (_ze_admin_space_connectivity_listener_ctx_t *)ctx; if (event == NULL || listener_ctx == NULL) { return; } _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(&listener_ctx->_session); if (_Z_RC_IS_NULL(&session_rc)) { return; } const z_loaned_transport_t *transport = z_transport_event_transport(event); if (transport == NULL || z_transport_is_multicast(transport)) { _z_session_rc_drop(&session_rc); return; } _z_session_t *session = _Z_RC_IN_VAL(&session_rc); z_id_t peer_zid = z_transport_zid(transport); z_owned_keyexpr_t ke; z_result_t ret = _ze_admin_space_session_transport_ke(&ke, &session->_local_zid, &peer_zid); if (ret != _Z_RES_OK) { _z_session_rc_drop(&session_rc); return; } if (z_transport_event_kind(event) == Z_SAMPLE_KIND_PUT) { z_owned_bytes_t payload; ret = _ze_admin_space_encode_connectivity_transport_payload(&payload, &peer_zid, z_transport_whatami(transport), z_transport_is_qos(transport), z_transport_is_shm(transport)); if (ret == _Z_RES_OK) { ret = _ze_admin_space_put_session_local_json(session, z_keyexpr_loan(&ke), &payload); } } else if (z_transport_event_kind(event) == Z_SAMPLE_KIND_DELETE) { ret = _ze_admin_space_delete_session_local(session, z_keyexpr_loan(&ke)); } else { ret = _Z_RES_OK; } if (ret != _Z_RES_OK) { _Z_WARN("Failed to publish RFC connectivity transport event: %d", ret); } z_keyexpr_drop(z_keyexpr_move(&ke)); _z_session_rc_drop(&session_rc); } static void _ze_admin_space_publish_link_event(z_loaned_link_event_t *event, void *ctx) { _ze_admin_space_connectivity_listener_ctx_t *listener_ctx = (_ze_admin_space_connectivity_listener_ctx_t *)ctx; if (event == NULL || listener_ctx == NULL) { return; } _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(&listener_ctx->_session); if (_Z_RC_IS_NULL(&session_rc)) { return; } _z_session_t *session = _Z_RC_IN_VAL(&session_rc); _z_session_transport_mutex_lock(session); bool is_unicast_transport = session->_tp._type == _Z_TRANSPORT_UNICAST_TYPE; _z_session_transport_mutex_unlock(session); if (!is_unicast_transport) { _z_session_rc_drop(&session_rc); return; } const z_loaned_link_t *link = z_link_event_link(event); if (link == NULL) { _z_session_rc_drop(&session_rc); return; } z_id_t peer_zid = z_link_zid(link); z_owned_keyexpr_t ke; z_result_t ret = _ze_admin_space_session_link_ke(&ke, &session->_local_zid, &peer_zid, &peer_zid); if (ret != _Z_RES_OK) { _z_session_rc_drop(&session_rc); return; } if (z_link_event_kind(event) == Z_SAMPLE_KIND_PUT) { z_owned_string_t src; z_owned_string_t dst; z_internal_string_null(&src); z_internal_string_null(&dst); ret = z_link_src(link, &src); if (ret == _Z_RES_OK) { ret = z_link_dst(link, &dst); } if (ret == _Z_RES_OK) { z_owned_bytes_t payload; ret = _ze_admin_space_encode_connectivity_link_payload(&payload, z_string_loan(&src), z_string_loan(&dst), z_link_mtu(link), z_link_is_streamed(link), z_link_is_reliable(link)); if (ret == _Z_RES_OK) { ret = _ze_admin_space_put_session_local_json(session, z_keyexpr_loan(&ke), &payload); } } z_string_drop(z_string_move(&src)); z_string_drop(z_string_move(&dst)); } else if (z_link_event_kind(event) == Z_SAMPLE_KIND_DELETE) { ret = _ze_admin_space_delete_session_local(session, z_keyexpr_loan(&ke)); } else { ret = _Z_RES_OK; } if (ret != _Z_RES_OK) { _Z_WARN("Failed to publish RFC connectivity link event: %d", ret); } z_keyexpr_drop(z_keyexpr_move(&ke)); _z_session_rc_drop(&session_rc); } #endif // Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_PUBLICATION == 1 z_result_t zp_start_admin_space(z_loaned_session_t *zs) { _z_session_t *session = _Z_RC_IN_VAL(zs); z_id_t zid = z_info_zid(zs); z_result_t ret = _Z_RES_OK; _z_session_admin_space_mutex_lock(session); if (session->_admin_space_queryable_id != 0) { goto out; } _z_session_weak_t *pico_session_weak = _z_session_rc_clone_as_weak_ptr(zs); if (_Z_RC_IS_NULL(pico_session_weak)) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto out; } z_owned_keyexpr_t ke; ret = _ze_admin_space_pico_queryable_ke(&ke, &zid); if (ret != _Z_RES_OK) { _z_session_weak_drop(pico_session_weak); z_free(pico_session_weak); goto out; } z_owned_closure_query_t callback; ret = z_closure_query(&callback, _ze_admin_space_pico_query_handler, _ze_admin_space_query_dropper, pico_session_weak); if (ret != _Z_RES_OK) { z_keyexpr_drop(z_keyexpr_move(&ke)); _z_session_weak_drop(pico_session_weak); z_free(pico_session_weak); goto out; } z_owned_queryable_t admin_space_queryable; ret = z_declare_queryable(zs, &admin_space_queryable, z_keyexpr_loan(&ke), z_closure_query_move(&callback), NULL); z_keyexpr_drop(z_keyexpr_move(&ke)); if (ret != _Z_RES_OK) { goto out; } session->_admin_space_queryable_id = admin_space_queryable._val._entity_id; _z_queryable_clear(&admin_space_queryable._val); #if Z_FEATURE_CONNECTIVITY == 1 _z_session_weak_t *session_session_weak = _z_session_rc_clone_as_weak_ptr(zs); if (_Z_RC_IS_NULL(session_session_weak)) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto err_queryable; } ret = _ze_admin_space_session_queryable_ke(&ke, &zid); if (ret != _Z_RES_OK) { _z_session_weak_drop(session_session_weak); z_free(session_session_weak); goto err_queryable; } ret = _ze_admin_space_declare_session_queryable(zs, &session->_admin_space_session_queryable_id, z_keyexpr_loan(&ke), session_session_weak); z_keyexpr_drop(z_keyexpr_move(&ke)); if (ret != _Z_RES_OK) { goto err_queryable; } #if Z_FEATURE_PUBLICATION == 1 session->_admin_space_transport_listener_id = 0; session->_admin_space_link_listener_id = 0; _ze_admin_space_connectivity_listener_ctx_t *transport_ctx = _ze_admin_space_connectivity_listener_ctx_new(zs); if (transport_ctx == NULL) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto err_session_queryable; } z_owned_closure_transport_event_t transport_callback; ret = z_closure_transport_event(&transport_callback, _ze_admin_space_publish_transport_event, _ze_admin_space_connectivity_listener_ctx_drop, transport_ctx); if (ret != _Z_RES_OK) { _ze_admin_space_connectivity_listener_ctx_drop(transport_ctx); goto err_session_queryable; } z_owned_transport_events_listener_t transport_listener; z_transport_events_listener_options_t transport_opts; z_transport_events_listener_options_default(&transport_opts); ret = z_declare_transport_events_listener(zs, &transport_listener, z_closure_transport_event_move(&transport_callback), &transport_opts); if (ret != _Z_RES_OK) { goto err_session_queryable; } session->_admin_space_transport_listener_id = transport_listener._val._id; _ze_admin_space_transport_listener_handle_clear(&transport_listener); _ze_admin_space_connectivity_listener_ctx_t *link_ctx = _ze_admin_space_connectivity_listener_ctx_new(zs); if (link_ctx == NULL) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto err_transport_listener; } z_owned_closure_link_event_t link_callback; ret = z_closure_link_event(&link_callback, _ze_admin_space_publish_link_event, _ze_admin_space_connectivity_listener_ctx_drop, link_ctx); if (ret != _Z_RES_OK) { _ze_admin_space_connectivity_listener_ctx_drop(link_ctx); goto err_transport_listener; } z_owned_link_events_listener_t link_listener; z_link_events_listener_options_t link_opts; z_link_events_listener_options_default(&link_opts); ret = z_declare_link_events_listener(zs, &link_listener, z_closure_link_event_move(&link_callback), &link_opts); if (ret != _Z_RES_OK) { goto err_transport_listener; } session->_admin_space_link_listener_id = link_listener._val._id; _ze_admin_space_link_listener_handle_clear(&link_listener); #endif #endif ret = _Z_RES_OK; goto out; #if Z_FEATURE_CONNECTIVITY == 1 #if Z_FEATURE_PUBLICATION == 1 err_transport_listener: { z_result_t undeclare_transport_listener_ret = _ze_admin_space_undeclare_transport_listener(zs, session->_admin_space_transport_listener_id); if (undeclare_transport_listener_ret == _Z_RES_OK) { session->_admin_space_transport_listener_id = 0; } else if (ret == _Z_RES_OK) { ret = undeclare_transport_listener_ret; } } #endif err_session_queryable: { z_result_t undeclare_queryable_ret = _ze_admin_space_undeclare_queryable(zs, session->_admin_space_session_queryable_id); if (undeclare_queryable_ret == _Z_RES_OK) { session->_admin_space_session_queryable_id = 0; } else if (ret == _Z_RES_OK) { ret = undeclare_queryable_ret; } } #endif #if Z_FEATURE_CONNECTIVITY == 1 err_queryable: { z_result_t undeclare_queryable_ret = _ze_admin_space_undeclare_queryable(zs, session->_admin_space_queryable_id); if (undeclare_queryable_ret == _Z_RES_OK) { session->_admin_space_queryable_id = 0; } else if (ret == _Z_RES_OK) { ret = undeclare_queryable_ret; } } #endif out: _z_session_admin_space_mutex_unlock(session); return ret; } z_result_t zp_stop_admin_space(z_loaned_session_t *zs) { _z_session_t *session = _Z_RC_IN_VAL(zs); z_result_t ret = _Z_RES_OK; _z_session_admin_space_mutex_lock(session); uint32_t admin_space_queryable_id = session->_admin_space_queryable_id; #if Z_FEATURE_CONNECTIVITY == 1 uint32_t admin_space_session_queryable_id = session->_admin_space_session_queryable_id; #if Z_FEATURE_PUBLICATION == 1 size_t admin_space_transport_listener_id = session->_admin_space_transport_listener_id; size_t admin_space_link_listener_id = session->_admin_space_link_listener_id; if (admin_space_queryable_id == 0 && admin_space_session_queryable_id == 0 && admin_space_transport_listener_id == 0 && admin_space_link_listener_id == 0) { goto out; } #else if (admin_space_queryable_id == 0 && admin_space_session_queryable_id == 0) { goto out; } #endif #else if (admin_space_queryable_id == 0) { goto out; } #endif #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_PUBLICATION == 1 if (admin_space_transport_listener_id != 0) { z_result_t listener_ret = _ze_admin_space_undeclare_transport_listener(zs, admin_space_transport_listener_id); if (listener_ret == _Z_RES_OK) { session->_admin_space_transport_listener_id = 0; } else if (ret == _Z_RES_OK) { ret = listener_ret; } } if (admin_space_link_listener_id != 0) { z_result_t listener_ret = _ze_admin_space_undeclare_link_listener(zs, admin_space_link_listener_id); if (listener_ret == _Z_RES_OK) { session->_admin_space_link_listener_id = 0; } else if (ret == _Z_RES_OK) { ret = listener_ret; } } #endif #if Z_FEATURE_CONNECTIVITY == 1 if (admin_space_session_queryable_id != 0) { z_result_t queryable_ret = _ze_admin_space_undeclare_queryable(zs, admin_space_session_queryable_id); if (queryable_ret == _Z_RES_OK) { session->_admin_space_session_queryable_id = 0; } else if (ret == _Z_RES_OK) { ret = queryable_ret; } } #endif if (admin_space_queryable_id != 0) { z_result_t queryable_ret = _ze_admin_space_undeclare_queryable(zs, admin_space_queryable_id); if (queryable_ret == _Z_RES_OK) { session->_admin_space_queryable_id = 0; } else if (ret == _Z_RES_OK) { ret = queryable_ret; } } out: _z_session_admin_space_mutex_unlock(session); return ret; } #endif // Z_FEATURE_ADMIN_SPACE == 1 ================================================ FILE: src/api/advanced_publisher.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/advanced_publisher.h" #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/serialization.h" #include "zenoh-pico/collections/atomic.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_ADVANCED_PUBLICATION == 1 // Space for 10 digits + NULL #define ZE_ADVANCED_PUBLISHER_UINT32_STR_BUF_LEN 11 static void _ze_advanced_publisher_state_init(_ze_advanced_publisher_state_t *state) { state->_heartbeat_mode = ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE; state->_last_published_sn = 0; z_internal_publisher_null(&state->_publisher); state->_state_publisher_task_handle = _z_fut_handle_null(); state->_zn = _z_session_weak_null(); _z_atomic_size_init(&state->_seqnumber, 0); } static bool _ze_advanced_publisher_state_check(const _ze_advanced_publisher_state_t *state) { return state->_heartbeat_mode == ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE || z_internal_publisher_check(&state->_publisher); } void _ze_advanced_publisher_state_clear(_ze_advanced_publisher_state_t *state) { if (state->_heartbeat_mode != ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE && !_z_fut_handle_is_null(state->_state_publisher_task_handle)) { _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&state->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { _z_runtime_cancel_fut(&_Z_RC_IN_VAL(&sess_rc)->_runtime, &state->_state_publisher_task_handle); _z_session_rc_drop(&sess_rc); } state->_state_publisher_task_handle = _z_fut_handle_null(); z_undeclare_publisher(z_publisher_move(&state->_publisher)); } _z_session_weak_drop(&state->_zn); state->_heartbeat_mode = ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE; state->_last_published_sn = 0; } bool _ze_advanced_publisher_check(const _ze_advanced_publisher_t *pub) { return z_internal_publisher_check(&pub->_publisher) && (!pub->_has_liveliness || z_internal_liveliness_token_check(&pub->_liveliness)) && (_Z_RC_IS_NULL(&pub->_state) || _ze_advanced_publisher_state_check(_Z_RC_IN_VAL(&pub->_state))); } _ze_advanced_publisher_t _ze_advanced_publisher_null(void) { _ze_advanced_publisher_t publisher = {0}; z_internal_publisher_null(&publisher._publisher); z_internal_liveliness_token_null(&publisher._liveliness); publisher._state = _ze_advanced_publisher_state_rc_null(); return publisher; } z_result_t _ze_undeclare_advanced_publisher_clear(_ze_advanced_publisher_t *pub) { if (pub == NULL || !_ze_advanced_publisher_check(pub)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } z_result_t ret = z_undeclare_publisher(z_publisher_move(&pub->_publisher)); if (pub->_has_liveliness) { z_liveliness_token_drop(z_liveliness_token_move(&pub->_liveliness)); pub->_has_liveliness = false; } if (!_Z_RC_IS_NULL(&pub->_state)) { _ze_advanced_publisher_state_rc_drop(&pub->_state); } if (pub->_cache != NULL) { _ze_advanced_cache_free(&pub->_cache); } *pub = _ze_advanced_publisher_null(); return ret; } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX(ze, _ze_advanced_publisher_t, advanced_publisher, _ze_advanced_publisher_check, _ze_advanced_publisher_null, _ze_undeclare_advanced_publisher_clear) void ze_advanced_publisher_cache_options_default(ze_advanced_publisher_cache_options_t *options) { options->is_enabled = true; options->max_samples = 1; options->congestion_control = z_internal_congestion_control_default_push(); options->priority = z_priority_default(); options->is_express = false; options->_liveliness = false; } void ze_advanced_publisher_sample_miss_detection_options_default( ze_advanced_publisher_sample_miss_detection_options_t *options) { options->is_enabled = true; options->heartbeat_mode = ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_DEFAULT; options->heartbeat_period_ms = 0; } void ze_advanced_publisher_options_default(ze_advanced_publisher_options_t *options) { z_publisher_options_default(&options->publisher_options); ze_advanced_publisher_cache_options_default(&options->cache); options->cache.is_enabled = false; ze_advanced_publisher_sample_miss_detection_options_default(&options->sample_miss_detection); options->sample_miss_detection.is_enabled = false; options->publisher_detection = false; options->publisher_detection_metadata = NULL; } void ze_advanced_publisher_put_options_default(ze_advanced_publisher_put_options_t *options) { z_publisher_put_options_default(&options->put_options); } void ze_advanced_publisher_delete_options_default(ze_advanced_publisher_delete_options_t *options) { z_publisher_delete_options_default(&options->delete_options); } // suffix = KE_ADV_PREFIX / KE_PUB / ZID / [ EID | KE_UHLC ] / [ meta | KE_EMPTY] static z_result_t _ze_advanced_publisher_ke_suffix(z_owned_keyexpr_t *suffix, const z_entity_global_id_t *id, const _ze_advanced_publisher_sequencing_t sequencing, const z_loaned_keyexpr_t *metadata) { z_internal_keyexpr_null(suffix); _Z_RETURN_IF_ERR(_Z_KEYEXPR_APPEND_STR_ARRAY(suffix, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_PUB)); z_id_t zid = z_entity_global_id_zid(id); z_owned_string_t zid_str; _Z_CLEAN_RETURN_IF_ERR(z_id_to_string(&zid, &zid_str), z_keyexpr_drop(z_keyexpr_move(suffix))); _Z_CLEAN_RETURN_IF_ERR( _z_keyexpr_append_substr(suffix, z_string_data(z_string_loan(&zid_str)), z_string_len(z_string_loan(&zid_str))), z_string_drop(z_string_move(&zid_str)); z_keyexpr_drop(z_keyexpr_move(suffix))); z_string_drop(z_string_move(&zid_str)); if (sequencing == _ZE_ADVANCED_PUBLISHER_SEQUENCING_SEQUENCE_NUMBER) { char buffer[ZE_ADVANCED_PUBLISHER_UINT32_STR_BUF_LEN]; uint32_t eid = z_entity_global_id_eid(id); snprintf(buffer, sizeof(buffer), "%u", eid); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(suffix, buffer), z_keyexpr_drop(z_keyexpr_move(suffix))); } else { _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(suffix, _Z_KEYEXPR_UHLC), z_keyexpr_drop(z_keyexpr_move(suffix))); } if (metadata != NULL) { _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_suffix(suffix, metadata), z_keyexpr_drop(z_keyexpr_move(suffix))); } else { _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(suffix, _Z_KEYEXPR_EMPTY), z_keyexpr_drop(z_keyexpr_move(suffix))); } return _Z_RES_OK; } static _z_fut_fn_result_t _ze_advanced_publisher_heartbeat_handler(void *ctx, _z_executor_t *executor) { _ZP_UNUSED(executor); _ze_advanced_publisher_state_weak_t *state_weak_rc = (_ze_advanced_publisher_state_weak_t *)ctx; _ze_advanced_publisher_state_rc_t state_rc = _ze_advanced_publisher_state_weak_upgrade(state_weak_rc); if (_Z_RC_IS_NULL(&state_rc)) { // State has been dropped, nothing to do return _z_fut_fn_result_ready(); } unsigned long heartbeat_period_ms = state_rc._val->_heartbeat_period_ms; _ze_advanced_publisher_state_t *state = _Z_RC_IN_VAL(&state_rc); bool publish = false; uint32_t next_seq = (uint32_t)_z_atomic_size_load(&state->_seqnumber, _z_memory_order_relaxed); switch (state->_heartbeat_mode) { case ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_PERIODIC: publish = true; break; case ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_SPORADIC: // TODO: This doesn't account for sn wraparound publish = (next_seq > state->_last_published_sn) ? true : false; break; default: _Z_WARN("Failed to publish heartbeat, invalid mode: %d", state->_heartbeat_mode); return _z_fut_fn_result_ready(); }; if (publish) { z_owned_bytes_t payload; z_bytes_empty(&payload); ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_uint32(ze_serializer_loan_mut(&serializer), _z_seqnumber_prev(next_seq)); ze_serializer_finish(ze_serializer_move(&serializer), &payload); z_result_t res = z_publisher_put(z_publisher_loan(&state->_publisher), z_bytes_move(&payload), NULL); if (res != _Z_RES_OK) { _Z_WARN("Failed to publish heartbeat: %d", res); } state->_last_published_sn = next_seq; } _ze_advanced_publisher_state_rc_drop(&state_rc); return _z_fut_fn_result_wake_up_after(heartbeat_period_ms); } static void _ze_advanced_publisher_heartbeat_dropper(void *ctx) { _ze_advanced_publisher_state_weak_t *state_rc = (_ze_advanced_publisher_state_weak_t *)ctx; _ze_advanced_publisher_state_weak_drop(state_rc); z_free(state_rc); } z_result_t ze_declare_advanced_publisher(const z_loaned_session_t *zs, ze_owned_advanced_publisher_t *pub, const z_loaned_keyexpr_t *keyexpr, const ze_advanced_publisher_options_t *options) { pub->_val = _ze_advanced_publisher_null(); // Set options ze_advanced_publisher_options_t opt; ze_advanced_publisher_options_default(&opt); if (options != NULL) { opt = *options; } // Validate options early if (opt.sample_miss_detection.is_enabled && opt.sample_miss_detection.heartbeat_mode != ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE) { if (opt.sample_miss_detection.heartbeat_period_ms == 0) { _Z_ERROR("Failed to create advanced publisher: heatbeat_period_ms must be > 0"); return _Z_ERR_INVALID; } } _Z_RETURN_IF_ERR(z_declare_publisher(zs, &pub->_val._publisher, keyexpr, &opt.publisher_options)); z_entity_global_id_t id = z_publisher_id(z_publisher_loan(&pub->_val._publisher)); if (opt.sample_miss_detection.is_enabled) { pub->_val._sequencing = _ZE_ADVANCED_PUBLISHER_SEQUENCING_SEQUENCE_NUMBER; _ze_advanced_publisher_state_t *state = z_malloc(sizeof(_ze_advanced_publisher_state_t)); if (state == NULL) { z_publisher_drop(z_publisher_move(&pub->_val._publisher)); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _ze_advanced_publisher_state_init(state); pub->_val._state = _ze_advanced_publisher_state_rc_new(state); if (_Z_RC_IS_NULL(&pub->_val._state)) { _ze_advanced_publisher_state_clear(state); z_free(state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } else if (opt.cache.is_enabled) { pub->_val._sequencing = _ZE_ADVANCED_PUBLISHER_SEQUENCING_TIMESTAMP; } else { pub->_val._sequencing = _ZE_ADVANCED_PUBLISHER_SEQUENCING_NONE; } z_owned_keyexpr_t suffix; _Z_CLEAN_RETURN_IF_ERR( _ze_advanced_publisher_ke_suffix(&suffix, &id, pub->_val._sequencing, opt.publisher_detection_metadata), _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher))); if (opt.cache.is_enabled) { _ze_advanced_cache_t *cache = _ze_advanced_cache_new(zs, keyexpr, z_keyexpr_loan(&suffix), opt.cache); if (cache == NULL) { _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } pub->_val._cache = cache; } // Create joined key expression only if needed for liveliness token or state publisher if (opt.publisher_detection || (opt.sample_miss_detection.is_enabled && opt.sample_miss_detection.heartbeat_mode != ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE)) { z_owned_keyexpr_t ke; _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_join(&ke, keyexpr, z_keyexpr_loan(&suffix)), _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_cache_free(&pub->_val._cache)); // Declare liveliness token on keyexpr/suffix if (opt.publisher_detection) { _Z_CLEAN_RETURN_IF_ERR(z_liveliness_declare_token(zs, &pub->_val._liveliness, z_keyexpr_loan(&ke), NULL), z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_cache_free(&pub->_val._cache)); pub->_val._has_liveliness = true; } // Declare state publisher on keyexpr/suffix if (opt.sample_miss_detection.is_enabled && opt.sample_miss_detection.heartbeat_mode != ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_NONE) { _ze_advanced_publisher_state_t *state = _Z_RC_IN_VAL(&pub->_val._state); state->_heartbeat_period_ms = opt.sample_miss_detection.heartbeat_period_ms; state->_heartbeat_mode = opt.sample_miss_detection.heartbeat_mode; state->_zn = _z_session_rc_clone_as_weak(zs); state->_last_published_sn = (uint32_t)_z_atomic_size_load(&state->_seqnumber, _z_memory_order_relaxed); z_publisher_options_t heatbeat_opts; z_publisher_options_default(&heatbeat_opts); if (opt.sample_miss_detection.heartbeat_mode == ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_SPORADIC) { heatbeat_opts.congestion_control = Z_CONGESTION_CONTROL_BLOCK; } _Z_CLEAN_RETURN_IF_ERR(z_declare_publisher(zs, &state->_publisher, z_keyexpr_loan(&ke), &heatbeat_opts), z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_liveliness_token_drop(z_liveliness_token_move(&pub->_val._liveliness)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_cache_free(&pub->_val._cache)); _ze_advanced_publisher_state_weak_t *ctx = _ze_advanced_publisher_state_rc_clone_as_weak_ptr(&pub->_val._state); if (_Z_RC_IS_NULL(ctx)) { z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_liveliness_token_drop(z_liveliness_token_move(&pub->_val._liveliness)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_cache_free(&pub->_val._cache); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_fut_t f = _z_fut_null(); f._destroy_fn = _ze_advanced_publisher_heartbeat_dropper; f._fut_fn = _ze_advanced_publisher_heartbeat_handler; f._fut_arg = ctx; state->_state_publisher_task_handle = _z_runtime_spawn(&_Z_RC_IN_VAL(zs)->_runtime, &f); if (_z_fut_handle_is_null(state->_state_publisher_task_handle)) { z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_publisher_state_rc_drop(&pub->_val._state); z_publisher_drop(z_publisher_move(&pub->_val._publisher)); z_liveliness_token_drop(z_liveliness_token_move(&pub->_val._liveliness)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_cache_free(&pub->_val._cache); return _Z_ERR_FAILED_TO_SPAWN_TASK; } } z_keyexpr_drop(z_keyexpr_move(&ke)); } z_keyexpr_drop(z_keyexpr_move(&suffix)); return _Z_RES_OK; } static z_result_t _ze_advanced_publisher_sequencing_options(const ze_loaned_advanced_publisher_t *pub, z_source_info_t *source_info, z_timestamp_t *timestamp) { if (source_info == NULL || timestamp == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } const z_loaned_publisher_t *publisher = z_publisher_loan(&pub->_publisher); // Set sequence number if required if (pub->_sequencing == _ZE_ADVANCED_PUBLISHER_SEQUENCING_SEQUENCE_NUMBER) { z_entity_global_id_t publisher_id = z_publisher_id(publisher); uint32_t seqnumber = (uint32_t)_z_atomic_size_fetch_add(&_Z_RC_IN_VAL(&pub->_state)->_seqnumber, 1, _z_memory_order_relaxed); *source_info = z_source_info_new(&publisher_id, seqnumber); } // Set timestamp _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&publisher->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { _Z_CLEAN_RETURN_IF_ERR(z_timestamp_new(timestamp, &sess_rc), _z_session_rc_drop(&sess_rc)); _z_session_rc_drop(&sess_rc); } else { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } return _Z_RES_OK; } z_result_t ze_undeclare_advanced_publisher(ze_moved_advanced_publisher_t *pub) { return _ze_undeclare_advanced_publisher_clear(&pub->_this._val); } z_result_t ze_advanced_publisher_put(const ze_loaned_advanced_publisher_t *pub, z_moved_bytes_t *payload, const ze_advanced_publisher_put_options_t *options) { ze_advanced_publisher_put_options_t opt; ze_advanced_publisher_put_options_default(&opt); if (options != NULL) { opt = *options; } z_timestamp_t timestamp = _z_timestamp_null(); z_source_info_t si = _z_source_info_null(); _Z_RETURN_IF_ERR(_ze_advanced_publisher_sequencing_options(pub, &si, ×tamp)); opt.put_options.timestamp = ×tamp; opt.put_options.source_info = &si; return _z_publisher_put_impl(z_publisher_loan(&pub->_publisher), payload, &opt.put_options, pub->_cache); } z_result_t ze_advanced_publisher_delete(const ze_loaned_advanced_publisher_t *pub, const ze_advanced_publisher_delete_options_t *options) { ze_advanced_publisher_delete_options_t opt; ze_advanced_publisher_delete_options_default(&opt); if (options != NULL) { opt = *options; } z_timestamp_t timestamp = _z_timestamp_null(); z_source_info_t si = _z_source_info_null(); _Z_RETURN_IF_ERR(_ze_advanced_publisher_sequencing_options(pub, &si, ×tamp)); opt.delete_options.timestamp = ×tamp; opt.delete_options.source_info = &si; return _z_publisher_delete_impl(z_publisher_loan(&pub->_publisher), &opt.delete_options, pub->_cache); } const z_loaned_keyexpr_t *ze_advanced_publisher_keyexpr(const ze_loaned_advanced_publisher_t *pub) { return z_publisher_keyexpr(z_publisher_loan(&pub->_publisher)); } z_entity_global_id_t ze_advanced_publisher_id(const ze_loaned_advanced_publisher_t *pub) { return z_publisher_id(z_publisher_loan(&pub->_publisher)); } z_result_t ze_advanced_publisher_get_matching_status(const ze_loaned_advanced_publisher_t *pub, z_matching_status_t *matching_status) { return z_publisher_get_matching_status(z_publisher_loan(&pub->_publisher), matching_status); } z_result_t ze_advanced_publisher_declare_matching_listener(const ze_loaned_advanced_publisher_t *publisher, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback) { return z_publisher_declare_matching_listener(z_publisher_loan(&publisher->_publisher), matching_listener, callback); } z_result_t ze_advanced_publisher_declare_background_matching_listener(const ze_loaned_advanced_publisher_t *publisher, z_moved_closure_matching_status_t *callback) { return z_publisher_declare_background_matching_listener(z_publisher_loan(&publisher->_publisher), callback); } #endif ================================================ FILE: src/api/advanced_subscriber.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/advanced_subscriber.h" #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/serialization.h" #include "zenoh-pico/collections/seqnumber.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/string.h" #include "zenoh-pico/utils/time_range.h" #include "zenoh-pico/utils/uuid.h" #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 #define ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE 256 // Space for 10 digits + NULL #define ZE_ADVANCED_SUBSCRIBER_UINT32_STR_BUF_LEN 11 z_result_t _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(_ze_advanced_subscriber_state_t *state) { #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(z_mutex_lock(z_mutex_loan_mut(&state->_mutex))); #endif if (state->_is_undeclaring) { #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_unlock(z_mutex_loan_mut(&state->_mutex)); #endif return Z_ERR_CANCELLED; } return _Z_RES_OK; } void _ze_advanced_subscriber_state_unlock_mutex(_ze_advanced_subscriber_state_t *state) { #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_unlock(z_mutex_loan_mut(&state->_mutex)); #endif } void _ze_sample_miss_listener_clear(_ze_sample_miss_listener_t *listener) { _ze_advanced_subscriber_state_weak_drop(&listener->_statesref); *listener = _ze_sample_miss_listener_null(); } z_result_t _ze_sample_miss_listener_drop(_ze_sample_miss_listener_t *listener) { if (listener == NULL) { return _Z_ERR_INVALID; } _ze_advanced_subscriber_state_rc_t state_rc = _ze_advanced_subscriber_state_weak_upgrade(&listener->_statesref); if (_Z_RC_IS_NULL(&state_rc)) { return _Z_ERR_INVALID; } _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(&state_rc); z_result_t ret = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(state); if (ret != _Z_RES_OK) { _ze_advanced_subscriber_state_rc_drop(&state_rc); _ze_sample_miss_listener_clear(listener); return ret == Z_ERR_CANCELLED ? _Z_RES_OK : ret; } _ze_closure_miss_intmap_remove(&state->_miss_handlers, listener->_id); _ze_advanced_subscriber_state_unlock_mutex(state); _ze_advanced_subscriber_state_rc_drop(&state_rc); _ze_sample_miss_listener_clear(listener); return _Z_RES_OK; } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX(ze, _ze_sample_miss_listener_t, sample_miss_listener, _ze_sample_miss_listener_check, _ze_sample_miss_listener_null, _ze_sample_miss_listener_drop) static z_result_t _ze_advanced_subscriber_sequenced_state_init(_ze_advanced_subscriber_sequenced_state_t *state, const _z_session_weak_t *zn, const z_loaned_keyexpr_t *keyexpr, const _z_entity_global_id_t *id) { (void)(id); state->_zn = _z_session_weak_clone(zn); state->_has_last_delivered = false; state->_last_delivered = 0; state->_pending_queries = 0; state->_periodic_query_handle = _z_fut_handle_null(); _z_uint32__z_sample_sortedmap_init(&state->_pending_samples); z_id_t zid = z_entity_global_id_zid(id); z_owned_string_t zid_str; _Z_RETURN_IF_ERR(z_id_to_string(&zid, &zid_str)); char buffer[ZE_ADVANCED_SUBSCRIBER_UINT32_STR_BUF_LEN]; uint32_t eid = z_entity_global_id_eid(id); snprintf(buffer, sizeof(buffer), "%u", eid); // Initialize query_keyexpr to: keyexpr / _Z_KEYEXPR_ADV_PREFIX / _Z_KEYEXPR_STAR / ZID / EID / _Z_KEYEXPR_STARSTAR z_internal_keyexpr_null(&state->_query_keyexpr); _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&state->_query_keyexpr, keyexpr), z_string_drop(z_string_move(&zid_str))); _Z_CLEAN_RETURN_IF_ERR(_Z_KEYEXPR_APPEND_STR_ARRAY(&state->_query_keyexpr, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_STAR), z_string_drop(z_string_move(&zid_str)); z_keyexpr_drop(z_keyexpr_move(&state->_query_keyexpr))); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_substr(&state->_query_keyexpr, z_string_data(z_string_loan(&zid_str)), z_string_len(z_string_loan(&zid_str))), z_string_drop(z_string_move(&zid_str)); z_keyexpr_drop(z_keyexpr_move(&state->_query_keyexpr))); z_string_drop(z_string_move(&zid_str)); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(&state->_query_keyexpr, buffer), z_keyexpr_drop(z_keyexpr_move(&state->_query_keyexpr))); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(&state->_query_keyexpr, _Z_KEYEXPR_STARSTAR), z_keyexpr_drop(z_keyexpr_move(&state->_query_keyexpr))); return _Z_RES_OK; } void _ze_advanced_subscriber_sequenced_state_clear(_ze_advanced_subscriber_sequenced_state_t *state) { state->_has_last_delivered = false; state->_last_delivered = 0; state->_pending_queries = 0; if (!_z_fut_handle_is_null(state->_periodic_query_handle)) { _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&state->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { z_result_t res = _z_runtime_cancel_fut(&_Z_RC_IN_VAL(&sess_rc)->_runtime, &state->_periodic_query_handle); if (res != _Z_RES_OK) { _Z_WARN("Failed to remove periodic task, res: %d", res); } state->_periodic_query_handle = _z_fut_handle_null(); _z_session_rc_drop(&sess_rc); } } _z_session_weak_drop(&state->_zn); state->_zn = _z_session_weak_null(); _z_uint32__z_sample_sortedmap_clear(&state->_pending_samples); z_keyexpr_drop(z_keyexpr_move(&state->_query_keyexpr)); } static void _ze_advanced_subscriber_timestamped_state_init(_ze_advanced_subscriber_timestamped_state_t *state) { state->_has_last_delivered = false; state->_last_delivered = _z_timestamp_null(); state->_pending_queries = 0; _z_timestamp__z_sample_sortedmap_init(&state->_pending_samples); } void _ze_advanced_subscriber_timestamped_state_clear(_ze_advanced_subscriber_timestamped_state_t *state) { state->_has_last_delivered = false; _z_timestamp_clear(&state->_last_delivered); state->_pending_queries = 0; _z_timestamp__z_sample_sortedmap_clear(&state->_pending_samples); } _ze_advanced_subscriber_state_t _ze_advanced_subscriber_state_null(void) { _ze_advanced_subscriber_state_t state = {0}; state._zn = _z_session_weak_null(); z_internal_keyexpr_null(&state._keyexpr); z_internal_liveliness_token_null(&state._token); z_internal_cancellation_token_null(&state._cancellation_token); return state; } static z_result_t _ze_advanced_subscriber_state_init(_ze_advanced_subscriber_state_t *state, const _z_session_rc_t *zn, z_moved_closure_sample_t *callback, const z_loaned_keyexpr_t *keyexpr, const ze_advanced_subscriber_options_t *options) { *state = _ze_advanced_subscriber_state_null(); state->_next_id = 0; state->_global_pending_queries = options->history.is_enabled ? 1 : 0; _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_init(&state->_sequenced_states); _z_id__ze_advanced_subscriber_timestamped_state_hashmap_init(&state->_timestamped_states); state->_zn = _z_session_rc_clone_as_weak(zn); state->_retransmission = options->recovery.is_enabled; state->_has_period = options->recovery.last_sample_miss_detection.periodic_queries_period_ms != 0; state->_period_ms = options->recovery.last_sample_miss_detection.periodic_queries_period_ms; state->_history_depth = options->history.is_enabled ? options->history.max_samples : 0; state->_history_age = options->history.is_enabled ? options->history.max_age_ms : 0; state->_query_target = Z_QUERY_TARGET_DEFAULT; // Use default query timeout if not specified in options state->_query_timeout = (options->query_timeout_ms > 0) ? options->query_timeout_ms : Z_GET_TIMEOUT_DEFAULT; state->_callback = callback->_this._val.call; state->_dropper = callback->_this._val.drop; state->_ctx = callback->_this._val.context; z_internal_closure_sample_null(&callback->_this); _ze_closure_miss_intmap_init(&state->_miss_handlers); state->_has_token = false; #if Z_FEATURE_MULTI_THREAD == 1 z_result_t ret = z_mutex_init(&state->_mutex); if (ret != _Z_RES_OK) { if (state->_dropper != NULL) { state->_dropper(state->_ctx); } return ret; } #endif _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&state->_keyexpr, keyexpr), _ze_advanced_subscriber_state_clear(state)); _Z_CLEAN_RETURN_IF_ERR(z_cancellation_token_new(&state->_cancellation_token), _ze_advanced_subscriber_state_clear(state)); return _Z_RES_OK; } static z_result_t _ze_advanced_subscriber_state_new(_ze_advanced_subscriber_state_rc_t *rc_state, const _z_session_rc_t *zn, z_moved_closure_sample_t *callback, const z_loaned_keyexpr_t *keyexpr, const ze_advanced_subscriber_options_t *options) { _ze_advanced_subscriber_state_t state; _Z_RETURN_IF_ERR(_ze_advanced_subscriber_state_init(&state, zn, callback, keyexpr, options)); *rc_state = _ze_advanced_subscriber_state_rc_new_from_val(&state); if (_Z_RC_IS_NULL(rc_state)) { _ze_advanced_subscriber_state_clear(&state); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } void _ze_advanced_subscriber_state_clear(_ze_advanced_subscriber_state_t *state) { _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_clear(&state->_sequenced_states); _z_id__ze_advanced_subscriber_timestamped_state_hashmap_clear(&state->_timestamped_states); _ze_closure_miss_intmap_clear(&state->_miss_handlers); z_keyexpr_drop(z_keyexpr_move(&state->_keyexpr)); z_cancellation_token_drop(z_cancellation_token_move(&state->_cancellation_token)); if (state->_has_token) { z_liveliness_token_drop(z_liveliness_token_move(&state->_token)); } #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_drop(z_mutex_move(&state->_mutex)); #endif _z_session_weak_drop(&state->_zn); *state = _ze_advanced_subscriber_state_null(); } static bool _ze_advanced_subscriber_state_check(const _ze_advanced_subscriber_state_t *state) { return (!_Z_RC_IS_NULL(&state->_zn) && z_internal_keyexpr_check(&state->_keyexpr) && (!state->_has_token || z_internal_liveliness_token_check(&state->_token))); } bool _ze_advanced_subscriber_check(const _ze_advanced_subscriber_t *sub) { return (z_internal_subscriber_check(&sub->_subscriber) && (!sub->_has_liveliness_subscriber || z_internal_subscriber_check(&sub->_liveliness_subscriber)) && (!sub->_has_heartbeat_subscriber || z_internal_subscriber_check(&sub->_heartbeat_subscriber)) && !_Z_RC_IS_NULL(&sub->_state) && _ze_advanced_subscriber_state_check(_Z_RC_IN_VAL(&sub->_state))); } _ze_advanced_subscriber_t _ze_advanced_subscriber_null(void) { _ze_advanced_subscriber_t subscriber = {0}; z_internal_subscriber_null(&subscriber._subscriber); z_internal_subscriber_null(&subscriber._liveliness_subscriber); z_internal_subscriber_null(&subscriber._heartbeat_subscriber); subscriber._state = _ze_advanced_subscriber_state_rc_null(); return subscriber; } z_result_t _ze_advanced_subscriber_undeclare(_ze_advanced_subscriber_t *sub) { if (sub == NULL || !_ze_advanced_subscriber_check(sub)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } z_result_t ret = z_undeclare_subscriber(z_subscriber_move(&sub->_subscriber)); if (sub->_has_liveliness_subscriber) { z_result_t ret2 = z_undeclare_subscriber(z_subscriber_move(&sub->_liveliness_subscriber)); _Z_SET_IF_OK(ret, ret2) } if (sub->_has_heartbeat_subscriber) { z_result_t ret2 = z_undeclare_subscriber(z_subscriber_move(&sub->_heartbeat_subscriber)); _Z_SET_IF_OK(ret, ret2); } _ze_advanced_subscriber_state_rc_drop(&sub->_state); *sub = _ze_advanced_subscriber_null(); return ret; } void _ze_advanced_subscriber_clear(_ze_advanced_subscriber_t *sub) { if (sub == NULL || !_ze_advanced_subscriber_check(sub)) { return; } _z_subscriber_clear(&sub->_subscriber._val); if (sub->_has_liveliness_subscriber) { _z_subscriber_clear(&sub->_liveliness_subscriber._val); } if (sub->_has_heartbeat_subscriber) { _z_subscriber_clear(&sub->_heartbeat_subscriber._val); } _ze_advanced_subscriber_state_rc_drop(&sub->_state); *sub = _ze_advanced_subscriber_null(); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL_PREFIX(ze, _ze_advanced_subscriber_t, advanced_subscriber, _ze_advanced_subscriber_check, _ze_advanced_subscriber_null, _ze_advanced_subscriber_undeclare) static bool _ze_advanced_subscriber_populate_query_params(char *buf, size_t buf_len, size_t max_samples, uint64_t max_age_ms, const _z_query_param_range_t *seq_range) { if (buf == NULL || buf_len == 0) { return false; } size_t pos = 0; // Allow responses for any key expression if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_KEY_ANYKE, _Z_QUERY_PARAMS_KEY_ANYKE_LEN)) { return false; // Not enough space } if (max_samples > 0) { if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_LIST_SEPARATOR, _Z_QUERY_PARAMS_LIST_SEPARATOR_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_KEY_MAX, _Z_QUERY_PARAMS_KEY_MAX_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_FIELD_SEPARATOR, _Z_QUERY_PARAMS_FIELD_SEPARATOR_LEN)) { return false; // Not enough space } int written = snprintf(&buf[pos], buf_len - pos, "%zu", max_samples); if (written < 0 || (size_t)written >= buf_len - pos) { return false; // Overflow or encoding error } pos += (size_t)written; } if (max_age_ms > 0) { if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_LIST_SEPARATOR, _Z_QUERY_PARAMS_LIST_SEPARATOR_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_KEY_TIME, _Z_QUERY_PARAMS_KEY_TIME_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_FIELD_SEPARATOR, _Z_QUERY_PARAMS_FIELD_SEPARATOR_LEN)) { return false; // Not enough space } double max_age_s = (double)max_age_ms * _Z_TIME_RANGE_MS_TO_SECS; _z_time_range_t range = {.start = {.bound = _Z_TIME_BOUND_INCLUSIVE, .now_offset = -max_age_s}, .end = {.bound = _Z_TIME_BOUND_UNBOUNDED}}; if (!_z_time_range_to_str(&range, &buf[pos], buf_len - pos)) { return false; // Not enough space or invalid range } size_t used = strnlen(&buf[pos], buf_len - pos); if (used == buf_len - pos) { // Null terminator not found in remaining space return false; } pos += used; } if (seq_range != NULL) { if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_LIST_SEPARATOR, _Z_QUERY_PARAMS_LIST_SEPARATOR_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_KEY_RANGE, _Z_QUERY_PARAMS_KEY_RANGE_LEN)) { return false; // Not enough space } if (!_z_memcpy_checked(buf, buf_len, &pos, _Z_QUERY_PARAMS_FIELD_SEPARATOR, _Z_QUERY_PARAMS_FIELD_SEPARATOR_LEN)) { return false; // Not enough space } if (seq_range->_has_start) { int written = snprintf(&buf[pos], buf_len - pos, "%u", seq_range->_start); if (written < 0 || (size_t)written >= buf_len - pos) { return false; // Overflow or encoding error } pos += (size_t)written; } if (buf_len - pos < 2) { return false; // Not enough room for ".." } buf[pos++] = '.'; buf[pos++] = '.'; if (seq_range->_has_end) { int written = snprintf(&buf[pos], buf_len - pos, "%u", seq_range->_end); if (written < 0 || (size_t)written >= buf_len - pos) { return false; // Overflow or encoding error } pos += (size_t)written; } } if (pos >= buf_len) { return false; // Not enough space for '\0' } buf[pos] = '\0'; return true; } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked static inline void __unsafe_ze_advanced_subscriber_trigger_miss_handler_callbacks( const _ze_closure_miss_intmap_t *miss_handlers, const z_entity_global_id_t *source_id, uint32_t nb) { _ze_closure_miss_intmap_iterator_t it = _ze_closure_miss_intmap_iterator_make(miss_handlers); ze_miss_t miss = {.source = *source_id, .nb = nb}; while (_ze_closure_miss_intmap_iterator_next(&it)) { _ze_closure_miss_t *closure = _ze_closure_miss_intmap_iterator_value(&it); if (closure != NULL && closure->call != NULL) { (closure->call)(&miss, closure->context); } } } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked static inline void __unsafe_ze_advanced_subscriber_deliver_and_flush(_z_sample_t *sample, uint32_t source_sn, _z_closure_sample_callback_t callback, void *ctx, _ze_advanced_subscriber_sequenced_state_t *state) { if (callback != NULL) { callback(sample, ctx); } state->_last_delivered = source_sn; state->_has_last_delivered = true; uint32_t next_sn = _z_seqnumber_next(source_sn); _z_sample_t *next_sample = _z_uint32__z_sample_sortedmap_get(&state->_pending_samples, &next_sn); while (next_sample != NULL) { if (callback != NULL) { callback(next_sample, ctx); } _z_uint32__z_sample_sortedmap_remove(&state->_pending_samples, &next_sn); state->_last_delivered = next_sn; next_sn = _z_seqnumber_next(next_sn); next_sample = _z_uint32__z_sample_sortedmap_get(&state->_pending_samples, &next_sn); } } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked // TODO: Handle sn wraparound static inline void __unsafe_ze_advanced_subscriber_flush_sequenced_source( _ze_advanced_subscriber_sequenced_state_t *state, _z_closure_sample_callback_t callback, void *ctx, const _z_entity_global_id_t *source_id, _ze_closure_miss_intmap_t *miss_handlers) { if (state->_pending_queries != 0 || _z_uint32__z_sample_sortedmap_is_empty(&state->_pending_samples)) { return; // Pending queries or no samples to deliver } _z_uint32__z_sample_sortedmap_iterator_t it = _z_uint32__z_sample_sortedmap_iterator_make(&state->_pending_samples); while (_z_uint32__z_sample_sortedmap_iterator_next(&it)) { const uint32_t *source_sn = _z_uint32__z_sample_sortedmap_iterator_key(&it); _z_sample_t *sample = _z_uint32__z_sample_sortedmap_iterator_value(&it); if (!state->_has_last_delivered) { state->_last_delivered = *source_sn; state->_has_last_delivered = true; if (callback != NULL) { callback(sample, ctx); } } else { uint32_t next_sn = _z_seqnumber_next(state->_last_delivered); int64_t diff = _z_seqnumber_diff(*source_sn, next_sn); if (diff >= 0) { if (diff > 0) { __unsafe_ze_advanced_subscriber_trigger_miss_handler_callbacks(miss_handlers, source_id, (uint32_t)diff); } state->_last_delivered = *source_sn; if (callback != NULL) { callback(sample, ctx); } } // else older or duplicate sample } } _z_uint32__z_sample_sortedmap_clear(&state->_pending_samples); } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked static inline void __unsafe_ze_advanced_subscriber_flush_timestamped_source( _ze_advanced_subscriber_timestamped_state_t *state, _z_closure_sample_callback_t callback, void *ctx) { if (state->_pending_queries == 0 && !_z_timestamp__z_sample_sortedmap_is_empty(&state->_pending_samples)) { _z_timestamp__z_sample_sortedmap_iterator_t it = _z_timestamp__z_sample_sortedmap_iterator_make(&state->_pending_samples); while (_z_timestamp__z_sample_sortedmap_iterator_next(&it)) { _z_timestamp_t *timestamp = _z_timestamp__z_sample_sortedmap_iterator_key(&it); _z_sample_t *sample = _z_timestamp__z_sample_sortedmap_iterator_value(&it); if (!state->_has_last_delivered || _z_timestamp_cmp(timestamp, &state->_last_delivered) > 0) { _z_timestamp_copy(&state->_last_delivered, timestamp); state->_has_last_delivered = true; if (callback != NULL) { callback(sample, ctx); } } } _z_timestamp__z_sample_sortedmap_clear(&state->_pending_samples); } } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked // Sets new_sequenced_source to true for new sequenced sources, false otherwise static z_result_t __unsafe_ze_advanced_subscriber_handle_sample(_ze_advanced_subscriber_state_t *states, z_loaned_sample_t *sample, bool *new_sequenced_source) { if (states == NULL || sample == NULL || new_sequenced_source == NULL) { _Z_ERROR("Invalid arguments to __unsafe_ze_advanced_subscriber_handle_sample"); _Z_ERROR_RETURN(_Z_ERR_INVALID); } *new_sequenced_source = false; const z_source_info_t *source_info = z_sample_source_info(sample); const z_timestamp_t *timestamp = z_sample_timestamp(sample); if (source_info != NULL) { z_entity_global_id_t id = z_source_info_id(source_info); uint32_t source_sn = z_source_info_sn(source_info); _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &id); if (state == NULL) { *new_sequenced_source = true; z_entity_global_id_t *new_id = z_malloc(sizeof(z_entity_global_id_t)); if (new_id == NULL) { _Z_ERROR("Failed to allocate memory for new sequenced state ID"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *new_id = id; _ze_advanced_subscriber_sequenced_state_t *new_state = z_malloc(sizeof(_ze_advanced_subscriber_sequenced_state_t)); if (new_state == NULL) { _Z_ERROR("Failed to allocate memory for new sequenced state"); z_free(new_id); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_ze_advanced_subscriber_sequenced_state_init(new_state, &states->_zn, z_keyexpr_loan(&states->_keyexpr), &id), z_free(new_id); z_free(new_state)); state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_insert( &states->_sequenced_states, new_id, new_state); if (state == NULL) { _Z_ERROR("Failed to insert new sequenced state into hashmap"); z_free(new_id); z_free(new_state); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } if (!state->_has_last_delivered && states->_global_pending_queries != 0) { // Avoid going through the map if history_depth == 1 if (states->_history_depth == 1) { state->_last_delivered = source_sn; state->_has_last_delivered = true; if (states->_callback != NULL) { states->_callback(sample, states->_ctx); } } else { uint32_t *new_source_sn = z_malloc(sizeof(uint32_t)); if (new_source_sn == NULL) { _Z_ERROR("Failed to allocate memory for new source_sn"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *new_source_sn = source_sn; _z_sample_t *new_sample = z_malloc(sizeof(_z_sample_t)); if (new_sample == NULL) { _Z_ERROR("Failed to allocate memory for new sample"); z_free(new_source_sn); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_z_sample_copy(new_sample, sample), z_free(new_source_sn); z_free(new_sample)); _z_sample_t *entry = _z_uint32__z_sample_sortedmap_insert(&state->_pending_samples, new_source_sn, new_sample); if (entry == NULL) { _Z_ERROR("Failed to insert sample into sequenced state"); z_free(new_source_sn); z_free(new_sample); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // _history_depth = 0 = wait for all global queries to complete if (states->_history_depth > 0 && _z_uint32__z_sample_sortedmap_len(&state->_pending_samples) >= states->_history_depth) { _z_uint32__z_sample_sortedmap_entry_t *first_entry = _z_uint32__z_sample_sortedmap_pop_first(&state->_pending_samples); if (first_entry != NULL) { uint32_t *first_source_sn = _z_uint32__z_sample_sortedmap_entry_key(first_entry); _z_sample_t *first_sample = _z_uint32__z_sample_sortedmap_entry_val(first_entry); __unsafe_ze_advanced_subscriber_deliver_and_flush(first_sample, *first_source_sn, states->_callback, states->_ctx, state); _z_uint32__z_sample_sortedmap_entry_free(&first_entry); } } } } else if (state->_has_last_delivered) { uint32_t next_sn = _z_seqnumber_next(state->_last_delivered); if (source_sn == next_sn) { __unsafe_ze_advanced_subscriber_deliver_and_flush(sample, source_sn, states->_callback, states->_ctx, state); } else if (_z_seqnumber_diff(source_sn, next_sn) > 0) { if (states->_retransmission) { uint32_t *new_source_sn = z_malloc(sizeof(uint32_t)); if (new_source_sn == NULL) { _Z_ERROR("Failed to allocate memory for new source_sn"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *new_source_sn = source_sn; _z_sample_t *new_sample = z_malloc(sizeof(_z_sample_t)); if (new_sample == NULL) { _Z_ERROR("Failed to allocate memory for new sample"); z_free(new_source_sn); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_z_sample_copy(new_sample, sample), z_free(new_source_sn); z_free(new_sample)); _z_sample_t *entry = _z_uint32__z_sample_sortedmap_insert(&state->_pending_samples, new_source_sn, new_sample); if (entry == NULL) { _Z_ERROR("Failed to insert sample into sequenced state"); z_free(new_source_sn); z_free(new_sample); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } else { uint32_t nb = (uint32_t)_z_seqnumber_diff(source_sn, next_sn); if (nb > 0) { __unsafe_ze_advanced_subscriber_trigger_miss_handler_callbacks(&states->_miss_handlers, &id, nb); } state->_last_delivered = source_sn; if (states->_callback != NULL) { states->_callback(sample, states->_ctx); } } } } else { __unsafe_ze_advanced_subscriber_deliver_and_flush(sample, source_sn, states->_callback, states->_ctx, state); } } else if (timestamp != NULL) { z_id_t id = z_timestamp_id(timestamp); _ze_advanced_subscriber_timestamped_state_t *state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_get(&states->_timestamped_states, &id); if (state == NULL) { z_id_t *new_id = z_malloc(sizeof(z_id_t)); if (new_id == NULL) { _Z_ERROR("Failed to allocate memory for new timestamped state ID"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *new_id = id; _ze_advanced_subscriber_timestamped_state_t *new_state = z_malloc(sizeof(_ze_advanced_subscriber_timestamped_state_t)); if (new_state == NULL) { _Z_ERROR("Failed to allocate memory for new timestamped state"); z_free(new_id); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _ze_advanced_subscriber_timestamped_state_init(new_state); state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_insert(&states->_timestamped_states, new_id, new_state); if (state == NULL) { _Z_ERROR("Failed to insert new timestamped state into hashmap"); z_free(new_id); z_free(new_state); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } if (!state->_has_last_delivered || _z_timestamp_cmp(&state->_last_delivered, timestamp) < 0) { if ((states->_global_pending_queries == 0 && state->_pending_queries == 0) || states->_history_depth == 1) { state->_last_delivered = *timestamp; state->_has_last_delivered = true; if (states->_callback != NULL) { states->_callback(sample, states->_ctx); } } else { _z_sample_t *entry = _z_timestamp__z_sample_sortedmap_get(&state->_pending_samples, timestamp); if (entry == NULL) { z_timestamp_t *new_timestamp = z_malloc(sizeof(z_timestamp_t)); if (new_timestamp == NULL) { _Z_ERROR("Failed to allocate memory for new timestamp"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_timestamp_copy(new_timestamp, timestamp); _z_sample_t *new_sample = z_malloc(sizeof(_z_sample_t)); if (new_sample == NULL) { _Z_ERROR("Failed to allocate memory for new sample"); z_free(new_timestamp); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_z_sample_copy(new_sample, sample), z_free(new_timestamp); z_free(new_sample)); entry = _z_timestamp__z_sample_sortedmap_insert(&state->_pending_samples, new_timestamp, new_sample); if (entry == NULL) { _Z_ERROR("Failed to insert sample into timestamped state"); z_free(new_timestamp); z_free(new_sample); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } // _history_depth = 0 = query all history if (states->_history_depth > 0 && _z_timestamp__z_sample_sortedmap_len(&state->_pending_samples) >= states->_history_depth) { __unsafe_ze_advanced_subscriber_flush_timestamped_source(state, states->_callback, states->_ctx); } } } } else { if (states->_callback != NULL) { states->_callback(sample, states->_ctx); } } return _Z_RES_OK; } typedef enum { _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_INITIAL, _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_SEQUENCED, _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_TIMESTAMPED } _ze_advanced_subscriber_query_ctx_kind_t; typedef struct { _ze_advanced_subscriber_state_rc_t _statesref; _ze_advanced_subscriber_query_ctx_kind_t _kind; union { z_entity_global_id_t _source_id; z_id_t _id; }; } _ze_advanced_subscriber_query_ctx_t; static inline _ze_advanced_subscriber_query_ctx_t _ze_advanced_subscriber_query_ctx_null(void) { _ze_advanced_subscriber_query_ctx_t ctx = {0}; ctx._statesref = _ze_advanced_subscriber_state_rc_null(); return ctx; } void _ze_advanced_subscriber_query_reply_handler(z_loaned_reply_t *reply, void *ctx) { if (!z_reply_is_ok(reply)) { const z_loaned_reply_err_t *err = z_reply_err(reply); z_owned_string_t errstr; z_bytes_to_string(z_reply_err_payload(err), &errstr); _Z_ERROR("Failed to query samples: %.*s", (int)z_string_len(z_string_loan(&errstr)), z_string_data(z_string_loan(&errstr))); z_string_drop(z_string_move(&errstr)); return; } _ze_advanced_subscriber_query_ctx_t *query_ctx = (_ze_advanced_subscriber_query_ctx_t *)ctx; if (!_Z_RC_IS_NULL(&query_ctx->_statesref)) { _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(&query_ctx->_statesref); const z_loaned_sample_t *sample = z_reply_ok(reply); const z_loaned_keyexpr_t *keyexpr = z_sample_keyexpr(sample); if (z_keyexpr_intersects(z_keyexpr_loan(&states->_keyexpr), keyexpr)) { if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock mutex for query reply handling"); return; } z_owned_sample_t sample_copy; if (z_sample_clone(&sample_copy, sample) == _Z_RES_OK) { bool new_source = false; z_result_t ret = __unsafe_ze_advanced_subscriber_handle_sample(states, z_sample_loan_mut(&sample_copy), &new_source); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to handle sample: %i", ret); } z_sample_drop(z_sample_move(&sample_copy)); } else { _Z_ERROR("Failed to clone sample for query reply handling"); } _ze_advanced_subscriber_state_unlock_mutex(states); } } } // Forward declaration static z_result_t __unsafe_ze_advanced_subscriber_spawn_periodic_query(_ze_advanced_subscriber_sequenced_state_t *state, _ze_advanced_subscriber_state_rc_t *states_rc, const z_entity_global_id_t *source_id); // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked void __unsafe_ze_advanced_subscriber_initial_query_drop_handler(_ze_advanced_subscriber_state_t *states, _ze_advanced_subscriber_state_rc_t *rc_states) { states->_global_pending_queries = (states->_global_pending_queries > 0) ? (states->_global_pending_queries - 1) : 0; if (states->_global_pending_queries == 0) { for (_z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_iterator_t it = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_iterator_make( &states->_sequenced_states); _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_iterator_next(&it);) { _z_entity_global_id_t *source_id = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_iterator_key(&it); _ze_advanced_subscriber_sequenced_state_t *sequenced_state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_iterator_value(&it); __unsafe_ze_advanced_subscriber_flush_sequenced_source(sequenced_state, states->_callback, states->_ctx, source_id, &states->_miss_handlers); if (_z_fut_handle_is_null(sequenced_state->_periodic_query_handle)) { __unsafe_ze_advanced_subscriber_spawn_periodic_query(sequenced_state, rc_states, source_id); } } for (_z_id__ze_advanced_subscriber_timestamped_state_hashmap_iterator_t it = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_iterator_make(&states->_timestamped_states); _z_id__ze_advanced_subscriber_timestamped_state_hashmap_iterator_next(&it);) { _ze_advanced_subscriber_timestamped_state_t *timestamped_state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_iterator_value(&it); __unsafe_ze_advanced_subscriber_flush_timestamped_source(timestamped_state, states->_callback, states->_ctx); } } } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked void __unsafe_ze_advanced_subscriber_sequenced_query_drop_handler(_ze_advanced_subscriber_state_t *states, const z_entity_global_id_t *source_id) { _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, source_id); if (state != NULL) { state->_pending_queries = (state->_pending_queries > 0) ? (state->_pending_queries - 1) : 0; if (states->_global_pending_queries == 0) { __unsafe_ze_advanced_subscriber_flush_sequenced_source(state, states->_callback, states->_ctx, source_id, &states->_miss_handlers); } } } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked void __unsafe_ze_advanced_subscriber_timestamped_query_drop_handler(_ze_advanced_subscriber_state_t *states, const z_id_t *id) { _ze_advanced_subscriber_timestamped_state_t *state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_get(&states->_timestamped_states, id); if (state != NULL) { state->_pending_queries = (state->_pending_queries > 0) ? (state->_pending_queries - 1) : 0; if (states->_global_pending_queries == 0) { __unsafe_ze_advanced_subscriber_flush_timestamped_source(state, states->_callback, states->_ctx); } } } void _ze_advanced_subscriber_query_drop_handler(void *ctx) { _ze_advanced_subscriber_query_ctx_t *query_ctx = (_ze_advanced_subscriber_query_ctx_t *)ctx; if (!_Z_RC_IS_NULL(&query_ctx->_statesref)) { _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(&query_ctx->_statesref); #if Z_FEATURE_MULTI_THREAD == 1 if (z_mutex_lock(z_mutex_loan_mut(&states->_mutex)) == _Z_RES_OK) { #endif switch (query_ctx->_kind) { case _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_INITIAL: __unsafe_ze_advanced_subscriber_initial_query_drop_handler(states, &query_ctx->_statesref); break; case _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_SEQUENCED: __unsafe_ze_advanced_subscriber_sequenced_query_drop_handler(states, &query_ctx->_source_id); break; case _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_TIMESTAMPED: __unsafe_ze_advanced_subscriber_timestamped_query_drop_handler(states, &query_ctx->_id); break; default: _Z_ERROR("Drop handler called for unknown query kind."); }; #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_unlock(z_mutex_loan_mut(&states->_mutex)); } #endif _ze_advanced_subscriber_state_rc_drop(&query_ctx->_statesref); } z_free(ctx); } static void _ze_advanced_subscriber_query_ctx_free(_ze_advanced_subscriber_query_ctx_t *ctx) { if (ctx == NULL) { return; } if (!_Z_RC_IS_NULL(&ctx->_statesref)) { _ze_advanced_subscriber_state_rc_drop(&ctx->_statesref); } z_free(ctx); } static z_result_t _ze_advanced_subscriber_run_query(_ze_advanced_subscriber_query_ctx_t *ctx, _ze_advanced_subscriber_state_rc_t *rc_state, const z_loaned_keyexpr_t *keyexpr, const char *params) { if (_Z_RC_IS_NULL(rc_state)) { _Z_ERROR("Failed to run query - state is NULL"); // Query context is still locally owned until the reply closure is handed to z_get(). _ze_advanced_subscriber_query_ctx_free(ctx); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(rc_state); _Z_CLEAN_RETURN_IF_ERR(_ze_advanced_subscriber_state_rc_copy(&ctx->_statesref, rc_state), _ze_advanced_subscriber_query_ctx_free(ctx)); z_owned_closure_reply_t callback; z_closure_reply(&callback, _ze_advanced_subscriber_query_reply_handler, _ze_advanced_subscriber_query_drop_handler, ctx); z_get_options_t get_opts; z_get_options_default(&get_opts); get_opts.consolidation.mode = Z_CONSOLIDATION_MODE_NONE; get_opts.target = Z_QUERY_TARGET_ALL; get_opts.timeout_ms = state->_query_timeout; _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&state->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _ze_advanced_subscriber_query_ctx_free(ctx); _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_owned_cancellation_token_t ct; _Z_CLEAN_RETURN_IF_ERR(z_cancellation_token_clone(&ct, z_cancellation_token_loan(&state->_cancellation_token)), _z_session_rc_drop(&sess_rc); _ze_advanced_subscriber_query_ctx_free(ctx)); get_opts.cancellation_token = z_cancellation_token_move(&ct); // From this point on, the reply closure owns ctx and the drop handler must free it. z_result_t ret = z_get(&sess_rc, keyexpr, params, z_closure_reply_move(&callback), &get_opts); _z_session_rc_drop(&sess_rc); return ret; } static inline z_result_t _ze_advanced_subscriber_initial_query(_ze_advanced_subscriber_state_rc_t *state, const z_loaned_keyexpr_t *keyexpr, const char *params) { _ze_advanced_subscriber_query_ctx_t *ctx = z_malloc(sizeof(_ze_advanced_subscriber_query_ctx_t)); if (ctx == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *ctx = _ze_advanced_subscriber_query_ctx_null(); ctx->_kind = _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_INITIAL; return _ze_advanced_subscriber_run_query(ctx, state, keyexpr, params); } static inline z_result_t _ze_advanced_subscriber_sequenced_query(_ze_advanced_subscriber_state_rc_t *state, const z_loaned_keyexpr_t *keyexpr, const char *params, const z_entity_global_id_t *source_id) { _ze_advanced_subscriber_query_ctx_t *ctx = z_malloc(sizeof(_ze_advanced_subscriber_query_ctx_t)); if (ctx == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *ctx = _ze_advanced_subscriber_query_ctx_null(); ctx->_kind = _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_SEQUENCED; ctx->_source_id = *source_id; return _ze_advanced_subscriber_run_query(ctx, state, keyexpr, params); } static inline z_result_t _ze_advanced_subscriber_timestamped_query(_ze_advanced_subscriber_state_rc_t *state, const z_loaned_keyexpr_t *keyexpr, const char *params, const z_id_t *id) { _ze_advanced_subscriber_query_ctx_t *ctx = z_malloc(sizeof(_ze_advanced_subscriber_query_ctx_t)); if (ctx == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *ctx = _ze_advanced_subscriber_query_ctx_null(); ctx->_kind = _ZE_ADVANCED_SUBSCRIBER_QUERY_CTX_TIMESTAMPED; ctx->_id = *id; return _ze_advanced_subscriber_run_query(ctx, state, keyexpr, params); } typedef struct { z_entity_global_id_t source_id; _ze_advanced_subscriber_state_rc_t _statesref; _z_sync_group_notifier_t _task_finished_notifier; } _ze_advanced_subscriber_periodic_query_ctx_t; _ze_advanced_subscriber_periodic_query_ctx_t _ze_advanced_subscriber_periodic_query_ctx_null(void) { _ze_advanced_subscriber_periodic_query_ctx_t ctx = {0}; return ctx; } void _ze_advanced_subscriber_periodic_query_ctx_drop(_ze_advanced_subscriber_periodic_query_ctx_t *ctx) { if (ctx == NULL) { return; } _ze_advanced_subscriber_state_rc_drop(&ctx->_statesref); _z_sync_group_notifier_drop(&ctx->_task_finished_notifier); } void _ze_advanced_subscriber_periodic_query_ctx_free(_ze_advanced_subscriber_periodic_query_ctx_t **ctx) { if (ctx == NULL) { return; } _ze_advanced_subscriber_periodic_query_ctx_drop(*ctx); z_free(*ctx); *ctx = NULL; } static _z_fut_fn_result_t _ze_advanced_subscriber_periodic_query_handler(void *ctx, _z_executor_t *executor) { _ZP_UNUSED(executor); _ze_advanced_subscriber_periodic_query_ctx_t *query_ctx = (_ze_advanced_subscriber_periodic_query_ctx_t *)ctx; if (_Z_RC_IS_NULL(&query_ctx->_statesref)) { return _z_fut_fn_result_ready(); } _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(&query_ctx->_statesref); z_result_t res = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states); if (res != _Z_RES_OK) { _Z_WARN("Failed to lock mutex when running periodic query: %d", res); return _z_fut_fn_result_ready(); } // Global history still running? Don’t schedule a per-source query yet. if (states->_global_pending_queries != 0) { _ze_advanced_subscriber_state_unlock_mutex(states); return _z_fut_fn_result_wake_up_after(states->_period_ms); } _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &query_ctx->source_id); if (state == NULL) { _ze_advanced_subscriber_state_unlock_mutex(states); return _z_fut_fn_result_wake_up_after(states->_period_ms); } // Don’t pile up queries; wait until the previous one finishes. if (state->_pending_queries != 0) { // Nothing to do this tick. _ze_advanced_subscriber_state_unlock_mutex(states); return _z_fut_fn_result_wake_up_after(states->_period_ms); } char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; _z_query_param_range_t range = { ._has_start = state->_has_last_delivered, ._start = state->_has_last_delivered ? _z_seqnumber_next(state->_last_delivered) : 0u, ._has_end = false, ._end = 0}; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), 0, 0, &range)) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_WARN("Failed to prepare periodic query"); return _z_fut_fn_result_ready(); } state->_pending_queries++; _ze_advanced_subscriber_state_unlock_mutex(states); res = _ze_advanced_subscriber_sequenced_query(&query_ctx->_statesref, z_keyexpr_loan(&state->_query_keyexpr), params, &query_ctx->source_id); if (res != _Z_RES_OK) { _Z_WARN("Failed to run periodic query"); res = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states); if (res != _Z_RES_OK) { _Z_WARN("Failed to lock mutex for periodic query failure (%d)", res); return _z_fut_fn_result_ready(); } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &query_ctx->source_id); if (state != NULL) { state->_pending_queries--; } _ze_advanced_subscriber_state_unlock_mutex(states); } return _z_fut_fn_result_wake_up_after(states->_period_ms); } static void _ze_advanced_subscriber_periodic_query_dropper(void *ctx) { _ze_advanced_subscriber_periodic_query_ctx_t *query_ctx = (_ze_advanced_subscriber_periodic_query_ctx_t *)ctx; _ze_advanced_subscriber_periodic_query_ctx_free(&query_ctx); } // SAFETY: Must be called with _ze_advanced_subscriber_state_t mutex locked static z_result_t __unsafe_ze_advanced_subscriber_spawn_periodic_query(_ze_advanced_subscriber_sequenced_state_t *state, _ze_advanced_subscriber_state_rc_t *rc_states, const z_entity_global_id_t *source_id) { if (_Z_RC_IS_NULL(rc_states)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(rc_states); // Don’t schedule while already running or while a global history query is pending. if (!_z_fut_handle_is_null(state->_periodic_query_handle) || states->_global_pending_queries != 0) { return _Z_RES_OK; } // Don't post periodic query if period is not set if (!states->_has_period) { return _Z_RES_OK; } _z_sync_group_notifier_t notifier; _Z_RETURN_IF_ERR(_z_cancellation_token_get_notifier( z_cancellation_token_loan_mut(&states->_cancellation_token)->_val, ¬ifier)); _ze_advanced_subscriber_periodic_query_ctx_t *ctx = z_malloc(sizeof(_ze_advanced_subscriber_periodic_query_ctx_t)); if (ctx == NULL) { _z_sync_group_notifier_drop(¬ifier); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *ctx = _ze_advanced_subscriber_periodic_query_ctx_null(); ctx->source_id = *source_id; ctx->_statesref = _ze_advanced_subscriber_state_rc_clone(rc_states); ctx->_task_finished_notifier = notifier; if (_Z_RC_IS_NULL(&ctx->_statesref)) { _ze_advanced_subscriber_periodic_query_ctx_free(&ctx); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&states->_zn); #else _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&states->_zn); #endif if (_Z_RC_IS_NULL(&sess_rc)) { _ze_advanced_subscriber_periodic_query_ctx_free(&ctx); _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } _z_fut_t fut = _z_fut_null(); fut._fut_arg = ctx; fut._fut_fn = _ze_advanced_subscriber_periodic_query_handler; fut._destroy_fn = _ze_advanced_subscriber_periodic_query_dropper; state->_periodic_query_handle = _z_runtime_spawn(&_Z_RC_IN_VAL(&sess_rc)->_runtime, &fut); _z_session_rc_drop(&sess_rc); if (_z_fut_handle_is_null(state->_periodic_query_handle)) { _Z_ERROR_RETURN(_Z_ERR_FAILED_TO_SPAWN_TASK); } return _Z_RES_OK; } void _ze_advanced_subscriber_subscriber_callback(z_loaned_sample_t *sample, void *ctx) { _ze_advanced_subscriber_state_rc_t *rc_states = (_ze_advanced_subscriber_state_rc_t *)ctx; if (_Z_RC_IS_NULL(rc_states)) { return; } _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(rc_states); if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock subscriber callback mutex"); return; } bool new_source = false; z_result_t ret = __unsafe_ze_advanced_subscriber_handle_sample(states, sample, &new_source); if (ret != _Z_RES_OK) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to handle sample in subscriber callback: %i", ret); return; } const z_source_info_t *source_info = z_sample_source_info(sample); if (source_info == NULL) { _ze_advanced_subscriber_state_unlock_mutex(states); return; } z_entity_global_id_t source_id = z_source_info_id(source_info); _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &source_id); if (state != NULL && new_source) { __unsafe_ze_advanced_subscriber_spawn_periodic_query(state, rc_states, &source_id); } if (state != NULL && states->_retransmission && state->_pending_queries == 0 && !_z_uint32__z_sample_sortedmap_is_empty(&state->_pending_samples)) { char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; _z_query_param_range_t range = { ._has_start = state->_has_last_delivered, ._start = state->_has_last_delivered ? _z_seqnumber_next(state->_last_delivered) : 0u, ._has_end = false, ._end = 0}; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), 0, 0, &range)) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to prepare query for missing samples"); return; } state->_pending_queries++; _ze_advanced_subscriber_state_unlock_mutex(states); z_result_t res = _ze_advanced_subscriber_sequenced_query(rc_states, z_keyexpr_loan(&state->_query_keyexpr), params, &source_id); if (res != _Z_RES_OK) { _Z_ERROR("Failed to query for missing samples"); if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock mutex missing sample query failure (%d)", res); return; } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &source_id); if (state != NULL) { state->_pending_queries--; } _ze_advanced_subscriber_state_unlock_mutex(states); } return; } _ze_advanced_subscriber_state_unlock_mutex(states); } void _ze_advanced_subscriber_subscriber_drop_handler(void *ctx) { _ze_advanced_subscriber_state_rc_t *rc_state = (_ze_advanced_subscriber_state_rc_t *)ctx; if (!_Z_RC_IS_NULL(rc_state)) { _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(rc_state); if (state->_dropper != NULL) { state->_dropper(state->_ctx); } if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(state) != _Z_RES_OK) { _Z_WARN("Failed to lock mutex for subscriber drop handler"); return; } // signal undeclaring state to prevent new queries or miss handlers from being added state->_is_undeclaring = true; // Eagerly clear child state before dropping our last direct rc reference so all advanced // subscriber resources are released by the time z_close()/undeclare returns: sequenced // states cancel their periodic query tasks here, and miss handlers release user callback // resources here. // clear miss handlers to release user callbacks _ze_closure_miss_intmap_clear(&state->_miss_handlers); // clear sequenced state to remove any periodic query tasks _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_clear(&state->_sequenced_states); _z_id__ze_advanced_subscriber_timestamped_state_hashmap_clear(&state->_timestamped_states); _ze_advanced_subscriber_state_unlock_mutex(state); if (z_internal_cancellation_token_check(&state->_cancellation_token)) { z_cancellation_token_cancel(z_cancellation_token_loan_mut(&state->_cancellation_token)); } _ze_advanced_subscriber_state_rc_drop(rc_state); z_free(ctx); } } static bool _ze_advanced_subscriber_parse_liveliness_keyexpr_u32(const char *start, size_t len, uint32_t *out) { if (start == NULL || len == 0 || len >= 11 || out == NULL) { return false; // 10 digits max + '\0' } char buf[ZE_ADVANCED_SUBSCRIBER_UINT32_STR_BUF_LEN]; if (!_z_memcpy_checked(buf, sizeof(buf) - 1, NULL, start, len)) { return false; } buf[len] = '\0'; char *endptr = NULL; unsigned long val = strtoul(buf, &endptr, 10); // Reject if not fully parsed or value is out of uint32_t range if (endptr == buf || *endptr != '\0' || val > UINT32_MAX) { return false; } *out = (uint32_t)val; return true; } #define _ZE_ADVANCED_SUBSCRIBER_UHLC_EID 0 // suffix = _Z_KEYEXPR_ADV_PREFIX / Entity Type / ZID / [ EID | _Z_KEYEXPR_UHLC ] / [ meta | _Z_KEYEXPR_EMPTY ] // On successful return id will be populated with the publishers ZID and either the EID or 0 for _Z_KEYEXPR_UHLC static bool _ze_advanced_subscriber_parse_liveliness_keyexpr(const z_loaned_keyexpr_t *ke, _z_entity_global_id_t *id) { z_view_string_t view; if (z_keyexpr_as_view_string(ke, &view) != _Z_RES_OK) { return false; } _z_str_se_t str; str.start = z_string_data(z_view_string_loan(&view)); str.end = str.start + z_string_len(z_view_string_loan(&view)); _z_splitstr_t segments = {.s = str, .delimiter = "/"}; int segment_cnt = 0; while (segments.s.end != NULL && segment_cnt < 5) { _z_str_se_t segment = _z_splitstr_nextback(&segments); if (segment.start != NULL) { segment_cnt++; size_t segment_len = (size_t)(segment.end - segment.start); switch (segment_cnt) { case 2: { // EID | _Z_KEYEXPR_UHLC if ((segment_len == _Z_KEYEXPR_UHLC_LEN && strncmp(segment.start, _Z_KEYEXPR_UHLC, _Z_KEYEXPR_UHLC_LEN) == 0)) { id->eid = _ZE_ADVANCED_SUBSCRIBER_UHLC_EID; } else if (!_ze_advanced_subscriber_parse_liveliness_keyexpr_u32(segment.start, segment_len, &id->eid) || id->eid == 0) { // Invalid EID *id = _z_entity_global_id_null(); _Z_TRACE("Received liveliness key expression with invalid EID"); return false; } break; } case 3: { // ZID _z_string_t zid_str = _z_string_alias_substr(segment.start, segment_len); id->zid = _z_id_from_string(&zid_str); if (!_z_id_check(id->zid)) { // Invalid Zenoh ID *id = _z_entity_global_id_null(); _Z_TRACE("Received liveliness key expression with invalid ZID"); return false; } break; } case 5: { // _Z_KEYEXPR_ADV_PREFIX if (segment_len != _Z_KEYEXPR_ADV_PREFIX_LEN || strncmp(segment.start, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_ADV_PREFIX_LEN) != 0) { *id = _z_entity_global_id_null(); _Z_TRACE("Received liveliness key expression with invalid segment"); return false; } break; } } } } // Validate segment count if (segment_cnt < 5) { *id = _z_entity_global_id_null(); return false; } return true; } void _ze_advanced_subscriber_liveliness_callback(z_loaned_sample_t *sample, void *ctx) { if (z_sample_kind(sample) != Z_SAMPLE_KIND_PUT) { return; } _ze_advanced_subscriber_state_rc_t *rc_states = (_ze_advanced_subscriber_state_rc_t *)ctx; if (_Z_RC_IS_NULL(rc_states)) { _Z_ERROR("Liveliness subscriber callback called with NULL state reference"); return; } _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(rc_states); const z_loaned_keyexpr_t *ke = z_sample_keyexpr(sample); z_entity_global_id_t id = _z_entity_global_id_null(); // Parse key expression to extract ZID and EID if (!_ze_advanced_subscriber_parse_liveliness_keyexpr(ke, &id)) { z_view_string_t kestr; z_keyexpr_as_view_string(ke, &kestr); _Z_WARN("Received malformed liveliness token key expression: '%.*s'\n", (int)z_string_len(z_view_string_loan(&kestr)), z_string_data(z_view_string_loan(&kestr))); return; } if (id.eid == _ZE_ADVANCED_SUBSCRIBER_UHLC_EID) { if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock liveliness subscriber callback mutex"); return; } _ze_advanced_subscriber_timestamped_state_t *state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_get(&states->_timestamped_states, &id.zid); if (state == NULL) { z_id_t *new_zid = z_malloc(sizeof(z_id_t)); if (new_zid == NULL) { _Z_ERROR("Failed to allocate memory for new timestamped state ID"); _ze_advanced_subscriber_state_unlock_mutex(states); return; } *new_zid = id.zid; _ze_advanced_subscriber_timestamped_state_t *new_state = z_malloc(sizeof(_ze_advanced_subscriber_timestamped_state_t)); if (new_state == NULL) { _Z_ERROR("Failed to allocate memory for new timestamped state"); _ze_advanced_subscriber_state_unlock_mutex(states); z_free(new_zid); return; } _ze_advanced_subscriber_timestamped_state_init(new_state); state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_insert(&states->_timestamped_states, new_zid, new_state); if (state == NULL) { _Z_ERROR("Failed to insert new timestamped state into hashmap"); _ze_advanced_subscriber_state_unlock_mutex(states); z_free(new_zid); z_free(new_state); return; } } char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), states->_history_depth, states->_history_age, NULL)) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to prepare query for timestamped samples"); return; } state->_pending_queries++; _ze_advanced_subscriber_state_unlock_mutex(states); if (_ze_advanced_subscriber_timestamped_query(rc_states, ke, params, &id.zid) != _Z_RES_OK) { _Z_ERROR("Failed to query for timestamped samples"); z_result_t res = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states); if (res != _Z_RES_OK) { _Z_WARN("Failed to lock mutex to relock mutex after timestamped sample query failure (%d)", res); return; } state = _z_id__ze_advanced_subscriber_timestamped_state_hashmap_get(&states->_timestamped_states, &id.zid); if (state != NULL) { state->_pending_queries--; } _ze_advanced_subscriber_state_unlock_mutex(states); } return; } else { if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock mutex when processing liveliness subscriber callback"); return; } _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &id); bool new_source = false; if (state == NULL) { new_source = true; z_entity_global_id_t *new_id = z_malloc(sizeof(z_entity_global_id_t)); if (new_id == NULL) { _Z_ERROR("Failed to allocate memory for new sequenced state ID"); _ze_advanced_subscriber_state_unlock_mutex(states); return; } *new_id = id; _ze_advanced_subscriber_sequenced_state_t *new_state = z_malloc(sizeof(_ze_advanced_subscriber_sequenced_state_t)); if (new_state == NULL) { _Z_ERROR("Failed to allocate memory for new sequenced state"); _ze_advanced_subscriber_state_unlock_mutex(states); z_free(new_id); return; } if (_ze_advanced_subscriber_sequenced_state_init(new_state, &states->_zn, z_keyexpr_loan(&states->_keyexpr), &id) != _Z_RES_OK) { _Z_ERROR("Failed to initialize new sequenced state"); _ze_advanced_subscriber_state_unlock_mutex(states); z_free(new_id); z_free(new_state); return; } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_insert( &states->_sequenced_states, new_id, new_state); if (state == NULL) { _Z_ERROR("Failed to insert new sequenced state into hashmap"); _ze_advanced_subscriber_state_unlock_mutex(states); z_free(new_id); z_free(new_state); return; } } char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), states->_history_depth, states->_history_age, NULL)) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to prepare query for sequenced samples"); return; } if (state != NULL && new_source) { __unsafe_ze_advanced_subscriber_spawn_periodic_query(state, rc_states, &id); } state->_pending_queries++; _ze_advanced_subscriber_state_unlock_mutex(states); z_result_t res = _ze_advanced_subscriber_sequenced_query(rc_states, z_keyexpr_loan(&state->_query_keyexpr), params, &id); if (res != _Z_RES_OK) { _Z_ERROR("Failed to query for sequenced samples"); res = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states); if (res != _Z_RES_OK) { _Z_WARN("Failed to relock mutex after sequenced sample query failure (%d)", res); return; } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &id); if (state != NULL) { state->_pending_queries--; } _ze_advanced_subscriber_state_unlock_mutex(states); } return; } } void _ze_advanced_subscriber_liveliness_drop_handler(void *ctx) { _ze_advanced_subscriber_state_rc_t *rc_state = (_ze_advanced_subscriber_state_rc_t *)ctx; if (!_Z_RC_IS_NULL(rc_state)) { _ze_advanced_subscriber_state_rc_drop(rc_state); z_free(ctx); } } void _ze_advanced_subscriber_heartbeat_callback(z_loaned_sample_t *sample, void *ctx) { if (z_sample_kind(sample) != Z_SAMPLE_KIND_PUT) { return; } _ze_advanced_subscriber_state_rc_t *rc_states = (_ze_advanced_subscriber_state_rc_t *)ctx; if (_Z_RC_IS_NULL(rc_states)) { _Z_ERROR("Heartbeat subscriber callback called with NULL state reference"); return; } _ze_advanced_subscriber_state_t *states = _Z_RC_IN_VAL(rc_states); const z_loaned_keyexpr_t *ke = z_sample_keyexpr(sample); z_entity_global_id_t id = _z_entity_global_id_null(); // Parse key expression to extract ZID and EID if (!_ze_advanced_subscriber_parse_liveliness_keyexpr(ke, &id)) { z_view_string_t kestr; z_keyexpr_as_view_string(ke, &kestr); _Z_WARN("Received malformed heartbeat key expression: '%.*s'\n", (int)z_string_len(z_view_string_loan(&kestr)), z_string_data(z_view_string_loan(&kestr))); return; } const z_loaned_bytes_t *payload = z_sample_payload(sample); if (payload == NULL) { _Z_WARN("Received heartbeat with no payload"); return; } if (z_bytes_len(payload) < 4) { _Z_WARN("Received heartbeat with invalid payload length: %zu", z_bytes_len(payload)); return; } ze_deserializer_t deserializer = ze_deserializer_from_bytes(payload); uint32_t heartbeat_sn; if (ze_deserializer_deserialize_uint32(&deserializer, &heartbeat_sn) != _Z_RES_OK) { _Z_WARN("Failed to deserialize heartbeat sequence number"); return; } if (!ze_deserializer_is_done(&deserializer)) { _Z_WARN("Heartbeat payload contains unexpected data after sequence number"); return; } if (_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states) != _Z_RES_OK) { _Z_WARN("Failed to lock heartbeat subscriber callback mutex"); return; } _ze_advanced_subscriber_sequenced_state_t *state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &id); if (state == NULL) { if (states->_global_pending_queries > 0) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_TRACE("Skipping heartbeat due to pending global query"); return; } z_entity_global_id_t *new_id = z_malloc(sizeof(z_entity_global_id_t)); if (new_id == NULL) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to allocate memory for new sequenced state ID"); return; } *new_id = id; _ze_advanced_subscriber_sequenced_state_t *new_state = z_malloc(sizeof(_ze_advanced_subscriber_sequenced_state_t)); if (new_state == NULL) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to allocate memory for new sequenced state"); z_free(new_id); return; } z_result_t res = _ze_advanced_subscriber_sequenced_state_init(new_state, &states->_zn, z_keyexpr_loan(&states->_keyexpr), &id); if (res != _Z_RES_OK) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to initialize new sequenced state: %i", res); z_free(new_id); z_free(new_state); return; } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_insert(&states->_sequenced_states, new_id, new_state); if (state == NULL) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to insert new sequenced state into hashmap"); z_free(new_id); z_free(new_state); return; } } if (!state->_has_last_delivered || (_z_seqnumber_diff(heartbeat_sn, state->_last_delivered) > 0 && state->_pending_queries == 0)) { char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; _z_query_param_range_t range = { ._has_start = state->_has_last_delivered, ._start = state->_has_last_delivered ? _z_seqnumber_next(state->_last_delivered) : 0u, ._has_end = true, ._end = heartbeat_sn}; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), 0, 0, &range)) { _ze_advanced_subscriber_state_unlock_mutex(states); _Z_ERROR("Failed to prepare query for missing samples"); return; } state->_pending_queries++; _ze_advanced_subscriber_state_unlock_mutex(states); z_result_t res = _ze_advanced_subscriber_sequenced_query(rc_states, z_keyexpr_loan(&state->_query_keyexpr), params, &id); if (res != _Z_RES_OK) { _Z_ERROR("Failed to query for missing samples"); res = _ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(states); if (res != _Z_RES_OK) { _Z_WARN("failed to relock mutex after missing sample query failure (%d)", res); return; } state = _z_entity_global_id__ze_advanced_subscriber_sequenced_state_hashmap_get(&states->_sequenced_states, &id); if (state != NULL) { state->_pending_queries--; } _ze_advanced_subscriber_state_unlock_mutex(states); } return; } _ze_advanced_subscriber_state_unlock_mutex(states); } void _ze_advanced_subscriber_heartbeat_drop_handler(void *ctx) { _ze_advanced_subscriber_state_rc_t *rc_state = (_ze_advanced_subscriber_state_rc_t *)ctx; if (!_Z_RC_IS_NULL(rc_state)) { _ze_advanced_subscriber_state_rc_drop(rc_state); z_free(ctx); } } // suffix = KE_ADV_PREFIX / KE_SUB / ZID / EID / [ metadata | KE_EMPTY ] static z_result_t _ze_advanced_subscriber_ke_suffix(z_owned_keyexpr_t *suffix, const z_entity_global_id_t id, const z_loaned_keyexpr_t *metadata) { z_internal_keyexpr_null(suffix); _Z_RETURN_IF_ERR(_Z_KEYEXPR_APPEND_STR_ARRAY(suffix, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_SUB)); z_id_t zid = z_entity_global_id_zid(&id); z_owned_string_t zid_str; _Z_CLEAN_RETURN_IF_ERR(z_id_to_string(&zid, &zid_str), z_keyexpr_drop(z_keyexpr_move(suffix))); _Z_CLEAN_RETURN_IF_ERR( _z_keyexpr_append_substr(suffix, z_string_data(z_string_loan(&zid_str)), z_string_len(z_string_loan(&zid_str))), z_string_drop(z_string_move(&zid_str)); z_keyexpr_drop(z_keyexpr_move(suffix))); z_string_drop(z_string_move(&zid_str)); char buffer[ZE_ADVANCED_SUBSCRIBER_UINT32_STR_BUF_LEN]; uint32_t eid = z_entity_global_id_eid(&id); snprintf(buffer, sizeof(buffer), "%u", eid); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(suffix, buffer), z_keyexpr_drop(z_keyexpr_move(suffix))); if (metadata != NULL) { _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_suffix(suffix, metadata), z_keyexpr_drop(z_keyexpr_move(suffix))); } else { _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_append_str(suffix, _Z_KEYEXPR_EMPTY), z_keyexpr_drop(z_keyexpr_move(suffix))); } return _Z_RES_OK; } z_result_t ze_declare_advanced_subscriber(const z_loaned_session_t *zs, ze_owned_advanced_subscriber_t *sub, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, ze_advanced_subscriber_options_t *options) { sub->_val = _ze_advanced_subscriber_null(); // Set options ze_advanced_subscriber_options_t opt; ze_advanced_subscriber_options_default(&opt); if (options != NULL) { opt = *options; } // Create Advanced Subscriber state _Z_RETURN_IF_ERR(_ze_advanced_subscriber_state_new(&sub->_val._state, zs, callback, keyexpr, &opt)); // Common keyexpr for subscribing to publisher liveliness and heartbeat z_owned_keyexpr_t ke_pub; _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&ke_pub, keyexpr), _ze_advanced_subscriber_state_rc_drop(&sub->_val._state)); _Z_CLEAN_RETURN_IF_ERR( _Z_KEYEXPR_APPEND_STR_ARRAY(&ke_pub, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_PUB, _Z_KEYEXPR_STARSTAR), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_state_rc_drop(&sub->_val._state)); // Declare subscriber _ze_advanced_subscriber_state_rc_t *sub_state = _ze_advanced_subscriber_state_rc_clone_as_ptr(&sub->_val._state); if (_Z_RC_IS_NULL(sub_state)) { _ze_advanced_subscriber_state_rc_drop(&sub->_val._state); z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } z_owned_closure_sample_t subscriber_callback; z_closure_sample(&subscriber_callback, _ze_advanced_subscriber_subscriber_callback, _ze_advanced_subscriber_subscriber_drop_handler, sub_state); _Z_CLEAN_RETURN_IF_ERR(z_declare_subscriber(zs, &sub->_val._subscriber, keyexpr, z_closure_sample_move(&subscriber_callback), &opt.subscriber_options), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_state_rc_drop(&sub->_val._state)); if (opt.history.is_enabled) { // Query initial history char params[ZE_ADVANCED_SUBSCRIBER_QUERY_PARAM_BUF_SIZE]; if (!_ze_advanced_subscriber_populate_query_params(params, sizeof(params), opt.history.max_samples, opt.history.max_age_ms, NULL)) { z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_owned_keyexpr_t query_keyexpr; _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&query_keyexpr, keyexpr), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); _Z_CLEAN_RETURN_IF_ERR(_Z_KEYEXPR_APPEND_STR_ARRAY(&query_keyexpr, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_STARSTAR), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); z_keyexpr_drop(z_keyexpr_move(&query_keyexpr)); _ze_advanced_subscriber_undeclare(&sub->_val)); _Z_CLEAN_RETURN_IF_ERR( _ze_advanced_subscriber_initial_query(&sub->_val._state, z_keyexpr_loan(&query_keyexpr), params), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); z_keyexpr_drop(z_keyexpr_move(&query_keyexpr)); _ze_advanced_subscriber_undeclare(&sub->_val)); z_keyexpr_drop(z_keyexpr_move(&query_keyexpr)); // Declare liveliness subscriber on keyexpr / KE_ADV_PREFIX / KE_PUB / KE_STARSTAR if (opt.history.detect_late_publishers) { z_liveliness_subscriber_options_t liveliness_options; z_liveliness_subscriber_options_default(&liveliness_options); liveliness_options.history = true; _ze_advanced_subscriber_state_rc_t *liveliness_sub_state = _ze_advanced_subscriber_state_rc_clone_as_ptr(&sub->_val._state); if (_Z_RC_IS_NULL(liveliness_sub_state)) { z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } z_owned_closure_sample_t liveliness_callback; _Z_CLEAN_RETURN_IF_ERR( z_closure_sample(&liveliness_callback, _ze_advanced_subscriber_liveliness_callback, _ze_advanced_subscriber_liveliness_drop_handler, liveliness_sub_state), _ze_advanced_subscriber_state_rc_drop(liveliness_sub_state); z_free(liveliness_sub_state); z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); _Z_CLEAN_RETURN_IF_ERR( z_liveliness_declare_subscriber(zs, &sub->_val._liveliness_subscriber, z_keyexpr_loan(&ke_pub), z_closure_sample_move(&liveliness_callback), &liveliness_options), _ze_advanced_subscriber_state_rc_drop(liveliness_sub_state); z_free(liveliness_sub_state); z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); sub->_val._has_liveliness_subscriber = true; } } // Heartbeat subscriber if (opt.recovery.is_enabled && opt.recovery.last_sample_miss_detection.is_enabled && opt.recovery.last_sample_miss_detection.periodic_queries_period_ms == 0) { _ze_advanced_subscriber_state_rc_t *heartbeat_sub_state = _ze_advanced_subscriber_state_rc_clone_as_ptr(&sub->_val._state); if (_Z_RC_IS_NULL(heartbeat_sub_state)) { z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } z_owned_closure_sample_t heartbeat_callback; _Z_CLEAN_RETURN_IF_ERR(z_closure_sample(&heartbeat_callback, _ze_advanced_subscriber_heartbeat_callback, _ze_advanced_subscriber_heartbeat_drop_handler, heartbeat_sub_state), _ze_advanced_subscriber_state_rc_drop(heartbeat_sub_state); z_free(heartbeat_sub_state); z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); _Z_CLEAN_RETURN_IF_ERR(z_declare_subscriber(zs, &sub->_val._heartbeat_subscriber, z_keyexpr_loan(&ke_pub), z_closure_sample_move(&heartbeat_callback), NULL), _ze_advanced_subscriber_state_rc_drop(heartbeat_sub_state); z_free(heartbeat_sub_state); z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); sub->_val._has_heartbeat_subscriber = true; } // Declare liveliness token on keyexpr/suffix if (opt.subscriber_detection) { _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(&sub->_val._state); z_entity_global_id_t id = z_subscriber_id(z_subscriber_loan(&sub->_val._subscriber)); z_owned_keyexpr_t suffix; _Z_CLEAN_RETURN_IF_ERR(_ze_advanced_subscriber_ke_suffix(&suffix, id, opt.subscriber_detection_metadata), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); _ze_advanced_subscriber_undeclare(&sub->_val)); z_owned_keyexpr_t ke; _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_join(&ke, keyexpr, z_keyexpr_loan(&suffix)), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); z_keyexpr_drop(z_keyexpr_move(&suffix)); _ze_advanced_subscriber_undeclare(&sub->_val)); #if Z_FEATURE_MULTI_THREAD == 1 _Z_CLEAN_RETURN_IF_ERR(z_mutex_lock(z_mutex_loan_mut(&state->_mutex)), z_keyexpr_drop(z_keyexpr_move(&ke_pub)); z_keyexpr_drop(z_keyexpr_move(&suffix)); z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_subscriber_undeclare(&sub->_val)); #endif z_result_t res = z_liveliness_declare_token(zs, &state->_token, z_keyexpr_loan(&ke), NULL); if (res != _Z_RES_OK) { #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_unlock(z_mutex_loan_mut(&state->_mutex)); #endif z_keyexpr_drop(z_keyexpr_move(&ke_pub)); z_keyexpr_drop(z_keyexpr_move(&suffix)); z_keyexpr_drop(z_keyexpr_move(&ke)); _ze_advanced_subscriber_undeclare(&sub->_val); _Z_ERROR_RETURN(res); } state->_has_token = true; #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_unlock(z_mutex_loan_mut(&state->_mutex)); #endif z_keyexpr_drop(z_keyexpr_move(&ke)); z_keyexpr_drop(z_keyexpr_move(&suffix)); } z_keyexpr_drop(z_keyexpr_move(&ke_pub)); return _Z_RES_OK; } z_result_t ze_declare_background_advanced_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, ze_advanced_subscriber_options_t *options) { ze_owned_advanced_subscriber_t sub; _Z_RETURN_IF_ERR(ze_declare_advanced_subscriber(zs, &sub, keyexpr, callback, options)); _ze_advanced_subscriber_clear(&sub._val); return _Z_RES_OK; } z_result_t ze_undeclare_advanced_subscriber(ze_moved_advanced_subscriber_t *sub) { return _ze_advanced_subscriber_undeclare(&sub->_this._val); } const z_loaned_keyexpr_t *ze_advanced_subscriber_keyexpr(const ze_loaned_advanced_subscriber_t *sub) { return z_subscriber_keyexpr(z_subscriber_loan(&sub->_subscriber)); } z_entity_global_id_t ze_advanced_subscriber_id(const ze_loaned_advanced_subscriber_t *sub) { return z_subscriber_id(z_subscriber_loan(&sub->_subscriber)); } z_result_t ze_advanced_subscriber_declare_sample_miss_listener(const ze_loaned_advanced_subscriber_t *subscriber, ze_owned_sample_miss_listener_t *sample_miss_listener, ze_moved_closure_miss_t *callback) { if (subscriber == NULL || _Z_RC_IS_NULL(&subscriber->_state)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } ze_internal_sample_miss_listener_null(sample_miss_listener); _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(&subscriber->_state); _Z_CLEAN_RETURN_IF_ERR(_ze_advanced_subscriber_state_lock_mutex_if_not_cancelled(state), _ze_closure_miss_drop(&callback->_this._val)); sample_miss_listener->_val._statesref = _ze_advanced_subscriber_state_rc_clone_as_weak(&subscriber->_state); if (_Z_RC_IS_NULL(&sample_miss_listener->_val._statesref)) { _ze_closure_miss_drop(&callback->_this._val); _ze_advanced_subscriber_state_unlock_mutex(state); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } sample_miss_listener->_val._id = state->_next_id++; _ze_closure_miss_t *closure = z_malloc(sizeof(_ze_closure_miss_t)); if (closure == NULL || _ze_closure_miss_intmap_insert(&state->_miss_handlers, sample_miss_listener->_val._id, closure) == NULL) { z_free(closure); _ze_closure_miss_drop(&callback->_this._val); _ze_sample_miss_listener_clear(&sample_miss_listener->_val); _ze_advanced_subscriber_state_unlock_mutex(state); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _ze_closure_miss_move(closure, &callback->_this._val); _ze_advanced_subscriber_state_unlock_mutex(state); return _Z_RES_OK; } z_result_t ze_advanced_subscriber_declare_background_sample_miss_listener( const ze_loaned_advanced_subscriber_t *subscriber, ze_moved_closure_miss_t *callback) { ze_owned_sample_miss_listener_t listener; _Z_RETURN_IF_ERR(ze_advanced_subscriber_declare_sample_miss_listener(subscriber, &listener, callback)); _ze_sample_miss_listener_clear(&listener._val); return _Z_RES_OK; } z_result_t ze_undeclare_sample_miss_listener(ze_moved_sample_miss_listener_t *sample_miss_listener) { return _ze_sample_miss_listener_drop(&sample_miss_listener->_this._val); } z_result_t ze_advanced_subscriber_detect_publishers(const ze_loaned_advanced_subscriber_t *subscriber, z_owned_subscriber_t *liveliness_subscriber, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options) { // Set options z_liveliness_subscriber_options_t opt; z_liveliness_subscriber_options_default(&opt); if (options != NULL) { opt = *options; } if (subscriber == NULL || _Z_RC_IS_NULL(&subscriber->_state)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } _ze_advanced_subscriber_state_t *state = _Z_RC_IN_VAL(&subscriber->_state); z_owned_keyexpr_t keyexpr; _Z_RETURN_IF_ERR(z_keyexpr_clone(&keyexpr, z_keyexpr_loan(&state->_keyexpr))); _Z_CLEAN_RETURN_IF_ERR( _Z_KEYEXPR_APPEND_STR_ARRAY(&keyexpr, _Z_KEYEXPR_ADV_PREFIX, _Z_KEYEXPR_PUB, _Z_KEYEXPR_STARSTAR), z_keyexpr_drop(z_keyexpr_move(&keyexpr))); _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&state->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { z_keyexpr_drop(z_keyexpr_move(&keyexpr)); _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_result_t ret = z_liveliness_declare_subscriber(&sess_rc, liveliness_subscriber, z_keyexpr_loan(&keyexpr), callback, &opt); z_keyexpr_drop(z_keyexpr_move(&keyexpr)); _z_session_rc_drop(&sess_rc); return ret; } z_result_t ze_advanced_subscriber_detect_publishers_background(const ze_loaned_advanced_subscriber_t *subscriber, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options) { z_owned_subscriber_t liveliness_subscriber; _Z_RETURN_IF_ERR(ze_advanced_subscriber_detect_publishers(subscriber, &liveliness_subscriber, callback, options)); _z_subscriber_clear(&liveliness_subscriber._val); return _Z_RES_OK; } void ze_advanced_subscriber_history_options_default(ze_advanced_subscriber_history_options_t *options) { options->is_enabled = true; options->detect_late_publishers = false; options->max_samples = 0; options->max_age_ms = 0; } void ze_advanced_subscriber_last_sample_miss_detection_options_default( ze_advanced_subscriber_last_sample_miss_detection_options_t *options) { options->is_enabled = true; options->periodic_queries_period_ms = 0; } void ze_advanced_subscriber_recovery_options_default(ze_advanced_subscriber_recovery_options_t *options) { options->is_enabled = true; ze_advanced_subscriber_last_sample_miss_detection_options_default(&options->last_sample_miss_detection); options->last_sample_miss_detection.is_enabled = false; } void ze_advanced_subscriber_options_default(ze_advanced_subscriber_options_t *options) { z_subscriber_options_default(&options->subscriber_options); ze_advanced_subscriber_history_options_default(&options->history); options->history.is_enabled = false; ze_advanced_subscriber_recovery_options_default(&options->recovery); options->recovery.is_enabled = false; options->query_timeout_ms = 0; options->subscriber_detection = false; options->subscriber_detection_metadata = NULL; } #endif ================================================ FILE: src/api/api.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include #include #include #include #include "zenoh-pico/api/admin_space.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/olv_macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/advanced_cache.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/net/config.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/logger.h" #include "zenoh-pico/net/matching.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" #include "zenoh-pico/utils/uuid.h" /********* Data Types Handlers *********/ z_result_t z_view_string_from_str(z_view_string_t *str, const char *value) { str->_val = _z_string_alias_str((char *)value); return _Z_RES_OK; } z_result_t z_view_string_from_substr(z_view_string_t *str, const char *value, size_t len) { str->_val = _z_string_alias_substr((char *)value, len); return _Z_RES_OK; } _z_string_svec_t _z_string_array_null(void) { return _z_string_svec_make(0); } void z_string_array_new(z_owned_string_array_t *a) { a->_val = _z_string_array_null(); } size_t z_string_array_push_by_alias(z_loaned_string_array_t *a, const z_loaned_string_t *value) { _z_string_t str = _z_string_alias(*value); _z_string_svec_append(a, &str, true); return _z_string_svec_len(a); } size_t z_string_array_push_by_copy(z_loaned_string_array_t *a, const z_loaned_string_t *value) { _z_string_t str; _z_string_copy(&str, value); _z_string_svec_append(a, &str, true); return _z_string_svec_len(a); } const z_loaned_string_t *z_string_array_get(const z_loaned_string_array_t *a, size_t k) { return _z_string_svec_get(a, k); } size_t z_string_array_len(const z_loaned_string_array_t *a) { return _z_string_svec_len(a); } bool z_string_array_is_empty(const z_loaned_string_array_t *a) { return _z_string_svec_is_empty(a); } z_result_t z_keyexpr_is_canon(const char *start, size_t len) { return _z_keyexpr_is_canon(start, len); } z_result_t z_keyexpr_canonize(char *start, size_t *len) { return _z_keyexpr_canonize(start, len); } z_result_t z_keyexpr_canonize_null_terminated(char *start) { size_t len = (start != NULL) ? strlen(start) : 0; z_result_t res = _z_keyexpr_canonize(start, &len); if (res == _Z_RES_OK) { start[len] = '\0'; } return res; } void z_view_keyexpr_from_str_unchecked(z_view_keyexpr_t *keyexpr, const char *name) { // SAFETY: Documentation specifies that string should be null-terminated. // Flawfinder: ignore [CWE-126] keyexpr->_val = _z_declared_keyexpr_alias_from_substr(name, strlen(name)); } z_result_t z_view_keyexpr_from_substr(z_view_keyexpr_t *keyexpr, const char *name, size_t len) { if (_z_keyexpr_is_canon(name, len) != Z_KEYEXPR_CANON_SUCCESS) { return Z_EINVAL; } keyexpr->_val = _z_declared_keyexpr_alias_from_substr(name, len); return _Z_RES_OK; } z_result_t z_view_keyexpr_from_str(z_view_keyexpr_t *keyexpr, const char *name) { size_t name_len = strlen(name); return z_view_keyexpr_from_substr(keyexpr, name, name_len); } z_result_t z_view_keyexpr_from_substr_autocanonize(z_view_keyexpr_t *keyexpr, char *name, size_t *len) { _Z_RETURN_IF_ERR(z_keyexpr_canonize(name, len)); keyexpr->_val = _z_declared_keyexpr_alias_from_substr(name, *len); return _Z_RES_OK; } z_result_t z_view_keyexpr_from_str_autocanonize(z_view_keyexpr_t *keyexpr, char *name) { size_t name_len = strlen(name); return z_view_keyexpr_from_substr_autocanonize(keyexpr, name, &name_len); } void z_view_keyexpr_from_substr_unchecked(z_view_keyexpr_t *keyexpr, const char *name, size_t len) { keyexpr->_val = _z_declared_keyexpr_alias_from_substr(name, len); } z_result_t z_keyexpr_as_view_string(const z_loaned_keyexpr_t *keyexpr, z_view_string_t *s) { s->_val = _z_string_alias(keyexpr->_inner._keyexpr); return _Z_RES_OK; } z_result_t z_keyexpr_concat(z_owned_keyexpr_t *key, const z_loaned_keyexpr_t *left, const char *right, size_t len) { return _z_declared_keyexpr_concat(&key->_val, left, right, len); } z_result_t z_keyexpr_join(z_owned_keyexpr_t *key, const z_loaned_keyexpr_t *left, const z_loaned_keyexpr_t *right) { return _z_declared_keyexpr_join(&key->_val, left, right); } z_result_t _z_keyexpr_append_suffix(z_owned_keyexpr_t *prefix, const z_loaned_keyexpr_t *right) { z_owned_keyexpr_t tmp; if (_z_string_len(&prefix->_val._inner._keyexpr) == 0) { if (_z_string_len(&right->_inner._keyexpr) == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } return z_keyexpr_clone(prefix, right); } _Z_RETURN_IF_ERR(z_keyexpr_join(&tmp, z_keyexpr_loan(prefix), right)); _z_declared_keyexpr_clear(&prefix->_val); z_keyexpr_take(prefix, z_keyexpr_move(&tmp)); return _Z_RES_OK; } z_result_t _z_keyexpr_append_substr(z_owned_keyexpr_t *prefix, const char *right, size_t len) { z_view_keyexpr_t ke_right; z_view_keyexpr_from_substr_unchecked(&ke_right, right, len); return _z_keyexpr_append_suffix(prefix, z_view_keyexpr_loan(&ke_right)); } z_result_t _z_keyexpr_append_str_array(z_owned_keyexpr_t *prefix, const char *strs[], size_t count) { for (size_t i = 0; i < count; ++i) { fflush(stdout); _Z_RETURN_IF_ERR(_z_keyexpr_append_str(prefix, strs[i])); } return _Z_RES_OK; } z_keyexpr_intersection_level_t z_keyexpr_relation_to(const z_loaned_keyexpr_t *left, const z_loaned_keyexpr_t *right) { if (z_keyexpr_equals(left, right)) { return Z_KEYEXPR_INTERSECTION_LEVEL_EQUALS; } else if (z_keyexpr_includes(left, right)) { return Z_KEYEXPR_INTERSECTION_LEVEL_INCLUDES; } else if (z_keyexpr_intersects(left, right)) { return Z_KEYEXPR_INTERSECTION_LEVEL_INTERSECTS; } return Z_KEYEXPR_INTERSECTION_LEVEL_DISJOINT; } bool z_keyexpr_includes(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r) { return _z_declared_keyexpr_includes(l, r); } bool z_keyexpr_intersects(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r) { return _z_declared_keyexpr_intersects(l, r); } bool z_keyexpr_equals(const z_loaned_keyexpr_t *l, const z_loaned_keyexpr_t *r) { return _z_declared_keyexpr_equals(l, r); } z_result_t z_config_default(z_owned_config_t *config) { return _z_config_default(&config->_val); } const char *zp_config_get(const z_loaned_config_t *config, uint8_t key) { return _z_config_get(config, key); } z_result_t zp_config_insert(z_loaned_config_t *config, uint8_t key, const char *value) { return _zp_config_insert(config, key, value); } _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_encoding_t, encoding, _z_encoding_check, _z_encoding_null, _z_encoding_copy, _z_encoding_move, _z_encoding_clear) bool z_encoding_equals(const z_loaned_encoding_t *left, const z_loaned_encoding_t *right) { return left->id == right->id && _z_string_equals(&left->schema, &right->schema); } z_result_t z_slice_copy_from_buf(z_owned_slice_t *slice, const uint8_t *data, size_t len) { slice->_val = _z_slice_copy_from_buf(data, len); if (slice->_val.start == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } else { return _Z_RES_OK; } } z_result_t z_slice_from_buf(z_owned_slice_t *slice, uint8_t *data, size_t len, void (*deleter)(void *data, void *context), void *context) { slice->_val = _z_slice_from_buf_custom_deleter(data, len, _z_delete_context_create(deleter, context)); return _Z_RES_OK; } z_result_t z_view_slice_from_buf(z_view_slice_t *slice, const uint8_t *data, size_t len) { slice->_val = _z_slice_alias_buf(data, len); return _Z_RES_OK; } const uint8_t *z_slice_data(const z_loaned_slice_t *slice) { return slice->start; } size_t z_slice_len(const z_loaned_slice_t *slice) { return slice->len; } void z_slice_empty(z_owned_slice_t *slice) { slice->_val = _z_slice_null(); } bool z_slice_is_empty(const z_loaned_slice_t *slice) { return _z_slice_is_empty(slice); } z_result_t z_bytes_to_slice(const z_loaned_bytes_t *bytes, z_owned_slice_t *dst) { dst->_val = _z_slice_null(); return _z_bytes_to_slice(bytes, &dst->_val); } z_result_t z_bytes_to_string(const z_loaned_bytes_t *bytes, z_owned_string_t *s) { // Init owned string z_internal_string_null(s); // Convert bytes to string size_t len = _z_bytes_len(bytes); if (len == 0) { return _Z_RES_OK; } s->_val = _z_string_preallocate(len); if (_z_string_len(&s->_val) != len) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); _z_bytes_to_buf(bytes, (uint8_t *)_z_string_data(&s->_val), len); return _Z_RES_OK; } z_result_t z_bytes_from_slice(z_owned_bytes_t *bytes, z_moved_slice_t *slice) { z_bytes_empty(bytes); _z_slice_t s = _z_slice_steal(&slice->_this._val); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_from_slice(&bytes->_val, &s), _z_slice_clear(&s)); return _Z_RES_OK; } z_result_t z_bytes_from_buf(z_owned_bytes_t *bytes, uint8_t *data, size_t len, void (*deleter)(void *data, void *context), void *context) { z_owned_slice_t s; s._val = _z_slice_from_buf_custom_deleter(data, len, _z_delete_context_create(deleter, context)); return z_bytes_from_slice(bytes, z_slice_move(&s)); } z_result_t z_bytes_from_static_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len) { z_owned_slice_t s; s._val = _z_slice_from_buf_custom_deleter(data, len, _z_delete_context_static()); return z_bytes_from_slice(bytes, z_slice_move(&s)); } z_result_t z_bytes_copy_from_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len) { z_owned_slice_t s; s._val = _z_slice_copy_from_buf(data, len); if (s._val.len != len) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return z_bytes_from_slice(bytes, z_slice_move(&s)); } z_result_t z_bytes_copy_from_slice(z_owned_bytes_t *bytes, const z_loaned_slice_t *slice) { z_owned_slice_t s; _Z_RETURN_IF_ERR(_z_slice_copy(&s._val, slice)); return z_bytes_from_slice(bytes, z_slice_move(&s)); } z_result_t z_bytes_from_string(z_owned_bytes_t *bytes, z_moved_string_t *s) { z_owned_slice_t slice; size_t str_len = _z_string_len(&s->_this._val); slice._val = _z_slice_steal(&s->_this._val._slice); slice._val.len = str_len; return z_bytes_from_slice(bytes, z_slice_move(&slice)); } z_result_t z_bytes_copy_from_string(z_owned_bytes_t *bytes, const z_loaned_string_t *value) { z_owned_string_t s; _Z_RETURN_IF_ERR(_z_string_copy(&s._val, value)); return z_bytes_from_string(bytes, z_string_move(&s)); } z_result_t z_bytes_from_str(z_owned_bytes_t *bytes, char *value, void (*deleter)(void *value, void *context), void *context) { z_owned_string_t s; s._val = _z_string_from_str_custom_deleter(value, _z_delete_context_create(deleter, context)); return z_bytes_from_string(bytes, z_string_move(&s)); } z_result_t z_bytes_copy_from_str(z_owned_bytes_t *bytes, const char *value) { z_owned_string_t s; s._val = _z_string_copy_from_str(value); if (s._val._slice.start == NULL && value != NULL) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return z_bytes_from_string(bytes, z_string_move(&s)); } z_result_t z_bytes_from_static_str(z_owned_bytes_t *bytes, const char *value) { z_owned_string_t s; s._val = _z_string_from_str_custom_deleter((char *)value, _z_delete_context_static()); return z_bytes_from_string(bytes, z_string_move(&s)); } void z_bytes_empty(z_owned_bytes_t *bytes) { bytes->_val = _z_bytes_null(); } size_t z_bytes_len(const z_loaned_bytes_t *bytes) { return _z_bytes_len(bytes); } bool z_bytes_is_empty(const z_loaned_bytes_t *bytes) { return _z_bytes_is_empty(bytes); } z_bytes_reader_t z_bytes_get_reader(const z_loaned_bytes_t *bytes) { return _z_bytes_get_reader(bytes); } size_t z_bytes_reader_read(z_bytes_reader_t *reader, uint8_t *dst, size_t len) { return _z_bytes_reader_read(reader, dst, len); } z_result_t z_bytes_reader_seek(z_bytes_reader_t *reader, int64_t offset, int origin) { return _z_bytes_reader_seek(reader, offset, origin); } int64_t z_bytes_reader_tell(z_bytes_reader_t *reader) { return _z_bytes_reader_tell(reader); } size_t z_bytes_reader_remaining(const z_bytes_reader_t *reader) { return _z_bytes_reader_remaining(reader); } z_bytes_slice_iterator_t z_bytes_get_slice_iterator(const z_loaned_bytes_t *bytes) { return (z_bytes_slice_iterator_t){._bytes = bytes, ._slice_idx = 0}; } #if defined(Z_FEATURE_UNSTABLE_API) z_result_t z_bytes_get_contiguous_view(const z_loaned_bytes_t *bytes, z_view_slice_t *view) { size_t num_slices = _z_bytes_num_slices(bytes); if (num_slices > 1) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } else if (num_slices == 1) { _z_arc_slice_t *slice = _z_bytes_get_slice(bytes, 0); z_view_slice_from_buf(view, _z_arc_slice_data(slice), _z_arc_slice_len(slice)); return _Z_RES_OK; } else { z_view_slice_empty(view); return _Z_RES_OK; } } #endif bool z_bytes_slice_iterator_next(z_bytes_slice_iterator_t *iter, z_view_slice_t *out) { if (iter->_slice_idx >= _z_bytes_num_slices(iter->_bytes)) { return false; } const _z_arc_slice_t *arc_slice = _z_bytes_get_slice(iter->_bytes, iter->_slice_idx); out->_val = _z_slice_alias_buf(_z_slice_simple_rc_value(&arc_slice->slice)->start + arc_slice->start, arc_slice->len); iter->_slice_idx++; return true; } void z_bytes_writer_finish(z_moved_bytes_writer_t *writer, z_owned_bytes_t *bytes) { bytes->_val = _z_bytes_writer_finish(&writer->_this._val); } z_result_t z_bytes_writer_empty(z_owned_bytes_writer_t *writer) { writer->_val = _z_bytes_writer_empty(); return _Z_RES_OK; } z_result_t z_bytes_writer_write_all(z_loaned_bytes_writer_t *writer, const uint8_t *src, size_t len) { return _z_bytes_writer_write_all(writer, src, len); } z_result_t z_bytes_writer_append(z_loaned_bytes_writer_t *writer, z_moved_bytes_t *bytes) { return _z_bytes_writer_append_z_bytes(writer, &bytes->_this._val); } z_result_t z_timestamp_new(z_timestamp_t *ts, const z_loaned_session_t *zs) { *ts = _z_timestamp_null(); _z_time_since_epoch t; _Z_RETURN_IF_ERR(_z_get_time_since_epoch(&t)); ts->valid = true; ts->time = _z_timestamp_ntp64_from_time(t.secs, t.nanos); ts->id = _Z_RC_IN_VAL(zs)->_local_zid; return _Z_RES_OK; } uint64_t z_timestamp_ntp64_time(const z_timestamp_t *ts) { return ts->time; } z_id_t z_timestamp_id(const z_timestamp_t *ts) { return ts->id; } z_result_t z_entity_global_id_new(z_entity_global_id_t *gid, const z_id_t *zid, uint32_t eid) { *gid = _z_entity_global_id_null(); gid->zid = *zid; gid->eid = eid; return _Z_RES_OK; } uint32_t z_entity_global_id_eid(const z_entity_global_id_t *gid) { return gid->eid; } z_id_t z_entity_global_id_zid(const z_entity_global_id_t *gid) { return gid->zid; } z_source_info_t z_source_info_new(const z_entity_global_id_t *source_id, uint32_t source_sn) { z_source_info_t si; si._source_id = *source_id; si._source_sn = source_sn; return si; } uint32_t z_source_info_sn(const z_source_info_t *info) { return info->_source_sn; } z_entity_global_id_t z_source_info_id(const z_source_info_t *info) { return info->_source_id; } z_query_target_t z_query_target_default(void) { return Z_QUERY_TARGET_DEFAULT; } z_reply_keyexpr_t z_reply_keyexpr_default(void) { return Z_REPLY_KEYEXPR_DEFAULT; } z_query_consolidation_t z_query_consolidation_auto(void) { return (z_query_consolidation_t){.mode = Z_CONSOLIDATION_MODE_AUTO}; } z_query_consolidation_t z_query_consolidation_latest(void) { return (z_query_consolidation_t){.mode = Z_CONSOLIDATION_MODE_LATEST}; } z_query_consolidation_t z_query_consolidation_monotonic(void) { return (z_query_consolidation_t){.mode = Z_CONSOLIDATION_MODE_MONOTONIC}; } z_query_consolidation_t z_query_consolidation_none(void) { return (z_query_consolidation_t){.mode = Z_CONSOLIDATION_MODE_NONE}; } z_query_consolidation_t z_query_consolidation_default(void) { return z_query_consolidation_auto(); } void z_query_parameters(const z_loaned_query_t *query, z_view_string_t *parameters) { parameters->_val = _z_string_alias(_Z_RC_IN_VAL(query)->_parameters); } z_reply_keyexpr_t z_query_accepts_replies(const z_loaned_query_t *query) { return _Z_RC_IN_VAL(query)->_anyke ? Z_REPLY_KEYEXPR_ANY : Z_REPLY_KEYEXPR_MATCHING_QUERY; } const z_loaned_bytes_t *z_query_attachment(const z_loaned_query_t *query) { return &_Z_RC_IN_VAL(query)->_attachment; } const z_loaned_keyexpr_t *z_query_keyexpr(const z_loaned_query_t *query) { return &_Z_RC_IN_VAL(query)->_key; } const z_loaned_bytes_t *z_query_payload(const z_loaned_query_t *query) { return &_Z_RC_IN_VAL(query)->_value.payload; } const z_source_info_t *z_query_source_info(const z_loaned_query_t *query) { const z_source_info_t *info = &_Z_RC_IN_VAL(query)->_source_info; return _z_source_info_check(info) ? info : NULL; } const z_loaned_encoding_t *z_query_encoding(const z_loaned_query_t *query) { return &_Z_RC_IN_VAL(query)->_value.encoding; } void z_closure_sample_call(const z_loaned_closure_sample_t *closure, z_loaned_sample_t *sample) { if (closure->call != NULL) { (closure->call)(sample, closure->context); } } void z_closure_query_call(const z_loaned_closure_query_t *closure, z_loaned_query_t *query) { if (closure->call != NULL) { (closure->call)(query, closure->context); } } void z_closure_reply_call(const z_loaned_closure_reply_t *closure, z_loaned_reply_t *reply) { if (closure->call != NULL) { (closure->call)(reply, closure->context); } } void z_closure_hello_call(const z_loaned_closure_hello_t *closure, z_loaned_hello_t *hello) { if (closure->call != NULL) { (closure->call)(hello, closure->context); } } void z_closure_zid_call(const z_loaned_closure_zid_t *closure, const z_id_t *id) { if (closure->call != NULL) { (closure->call)(id, closure->context); } } #if Z_FEATURE_CONNECTIVITY == 1 void z_closure_transport_call(const z_loaned_closure_transport_t *closure, z_loaned_transport_t *transport) { if (closure->call != NULL) { (closure->call)(transport, closure->context); } } void z_closure_link_call(const z_loaned_closure_link_t *closure, z_loaned_link_t *link) { if (closure->call != NULL) { (closure->call)(link, closure->context); } } void z_closure_transport_event_call(const z_loaned_closure_transport_event_t *closure, z_loaned_transport_event_t *event) { if (closure->call != NULL) { (closure->call)(event, closure->context); } } void z_closure_link_event_call(const z_loaned_closure_link_event_t *closure, z_loaned_link_event_t *event) { if (closure->call != NULL) { (closure->call)(event, closure->context); } } #endif void z_closure_matching_status_call(const z_loaned_closure_matching_status_t *closure, const z_matching_status_t *status) { if (closure->call != NULL) { (closure->call)(status, closure->context); } } void ze_closure_miss_call(const ze_loaned_closure_miss_t *closure, const ze_miss_t *miss) { if (closure->call != NULL) { (closure->call)(miss, closure->context); } } bool _z_config_check(const _z_config_t *config) { return !_z_str_intmap_is_empty(config); } _z_config_t _z_config_null(void) { return _z_str_intmap_make(); } z_result_t _z_config_copy(_z_config_t *dst, const _z_config_t *src) { *dst = _z_str_intmap_clone(src); return _Z_RES_OK; } void _z_config_drop(_z_config_t *config) { _z_str_intmap_clear(config); } _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_config_t, config, _z_config_check, _z_config_null, _z_config_copy, _z_str_intmap_move, _z_config_drop) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_string_t, string, _z_string_check, _z_string_null, _z_string_copy, _z_string_move, _z_string_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_value_t, reply_err, _z_value_check, _z_value_null, _z_value_copy, _z_value_move, _z_value_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_declared_keyexpr_t, keyexpr, _z_declared_keyexpr_check, _z_declared_keyexpr_null, _z_declared_keyexpr_copy, _z_declared_keyexpr_move, _z_declared_keyexpr_clear) _Z_VIEW_FUNCTIONS_IMPL(_z_declared_keyexpr_t, keyexpr, _z_declared_keyexpr_check, _z_declared_keyexpr_null) _Z_VIEW_FUNCTIONS_IMPL(_z_string_t, string, _z_string_check, _z_string_null) _Z_VIEW_FUNCTIONS_IMPL(_z_slice_t, slice, _z_slice_check, _z_slice_null) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_hello_t, hello, _z_hello_check, _z_hello_null, _z_hello_copy, _z_hello_move, _z_hello_clear) bool _z_string_array_check(const _z_string_svec_t *val) { return !_z_string_svec_is_empty(val); } z_result_t _z_string_array_copy(_z_string_svec_t *dst, const _z_string_svec_t *src) { return _z_string_svec_copy(dst, src, true); } _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_string_svec_t, string_array, _z_string_array_check, _z_string_array_null, _z_string_array_copy, _z_string_svec_move, _z_string_svec_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_slice_t, slice, _z_slice_check, _z_slice_null, _z_slice_copy, _z_slice_move, _z_slice_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_bytes_t, bytes, _z_bytes_check, _z_bytes_null, _z_bytes_copy, _z_bytes_move, _z_bytes_drop) _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL(_z_bytes_writer_t, bytes_writer, _z_bytes_writer_check, _z_bytes_writer_empty, _z_bytes_writer_move, _z_bytes_writer_clear) #if Z_FEATURE_PUBLICATION == 1 || Z_FEATURE_QUERYABLE == 1 || Z_FEATURE_QUERY == 1 // Convert a user owned bytes payload to an internal bytes payload, returning an empty one if value invalid static inline _z_bytes_t *_z_bytes_from_moved(z_moved_bytes_t *bytes) { return (bytes == NULL) ? NULL : &bytes->_this._val; } // Convert a user owned encoding to an internal encoding, return default encoding if value invalid static inline _z_encoding_t *_z_encoding_from_moved(z_moved_encoding_t *encoding) { return (encoding == NULL) ? NULL : &encoding->_this._val; } #endif _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_sample_t, sample, _z_sample_check, _z_sample_null, _z_sample_copy, _z_sample_move, _z_sample_clear) _Z_OWNED_FUNCTIONS_RC_IMPL_NO_DROP_CLONE(session) #if Z_FEATURE_CONNECTIVITY == 1 bool _z_info_transport_check(const _z_info_transport_t *transport) { return _z_id_check(transport->_zid); } _z_info_transport_t _z_info_transport_null(void) { return (_z_info_transport_t){0}; } z_result_t _z_info_transport_copy(_z_info_transport_t *dst, const _z_info_transport_t *src) { *dst = *src; return _Z_RES_OK; } z_result_t _z_info_transport_move(_z_info_transport_t *dst, _z_info_transport_t *src) { *dst = *src; *src = _z_info_transport_null(); return _Z_RES_OK; } void _z_info_transport_clear(_z_info_transport_t *transport) { *transport = _z_info_transport_null(); } bool _z_info_link_check(const _z_info_link_t *link) { return _z_id_check(link->_zid); } _z_info_link_t _z_info_link_null(void) { _z_info_link_t link = {0}; link._src = _z_string_null(); link._dst = _z_string_null(); return link; } z_result_t _z_info_link_copy(_z_info_link_t *dst, const _z_info_link_t *src) { *dst = _z_info_link_null(); dst->_zid = src->_zid; dst->_mtu = src->_mtu; dst->_is_streamed = src->_is_streamed; dst->_is_reliable = src->_is_reliable; _Z_RETURN_IF_ERR(_z_string_copy(&dst->_src, &src->_src)); _Z_CLEAN_RETURN_IF_ERR(_z_string_copy(&dst->_dst, &src->_dst), _z_string_clear(&dst->_src)); return _Z_RES_OK; } z_result_t _z_info_link_move(_z_info_link_t *dst, _z_info_link_t *src) { *dst = _z_info_link_null(); dst->_zid = src->_zid; dst->_mtu = src->_mtu; dst->_is_streamed = src->_is_streamed; dst->_is_reliable = src->_is_reliable; _Z_RETURN_IF_ERR(_z_string_move(&dst->_src, &src->_src)); _Z_CLEAN_RETURN_IF_ERR(_z_string_move(&dst->_dst, &src->_dst), _z_string_clear(&dst->_src)); *src = _z_info_link_null(); return _Z_RES_OK; } void _z_info_link_clear(_z_info_link_t *link) { _z_string_clear(&link->_src); _z_string_clear(&link->_dst); *link = _z_info_link_null(); } bool _z_info_transport_event_check(const _z_info_transport_event_t *event) { return _z_info_transport_check(&event->transport); } _z_info_transport_event_t _z_info_transport_event_null(void) { _z_info_transport_event_t event = {0}; event.kind = Z_SAMPLE_KIND_DEFAULT; event.transport = _z_info_transport_null(); return event; } z_result_t _z_info_transport_event_copy(_z_info_transport_event_t *dst, const _z_info_transport_event_t *src) { *dst = *src; return _Z_RES_OK; } z_result_t _z_info_transport_event_move(_z_info_transport_event_t *dst, _z_info_transport_event_t *src) { *dst = *src; *src = _z_info_transport_event_null(); return _Z_RES_OK; } void _z_info_transport_event_clear(_z_info_transport_event_t *event) { *event = _z_info_transport_event_null(); } bool _z_info_link_event_check(const _z_info_link_event_t *event) { return _z_info_link_check(&event->link); } _z_info_link_event_t _z_info_link_event_null(void) { _z_info_link_event_t event = {0}; event.kind = Z_SAMPLE_KIND_DEFAULT; event.link = _z_info_link_null(); return event; } z_result_t _z_info_link_event_copy(_z_info_link_event_t *dst, const _z_info_link_event_t *src) { *dst = _z_info_link_event_null(); dst->kind = src->kind; return _z_info_link_copy(&dst->link, &src->link); } z_result_t _z_info_link_event_move(_z_info_link_event_t *dst, _z_info_link_event_t *src) { *dst = _z_info_link_event_null(); dst->kind = src->kind; _Z_RETURN_IF_ERR(_z_info_link_move(&dst->link, &src->link)); src->kind = Z_SAMPLE_KIND_DEFAULT; return _Z_RES_OK; } void _z_info_link_event_clear(_z_info_link_event_t *event) { _z_info_link_clear(&event->link); event->kind = Z_SAMPLE_KIND_DEFAULT; } _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_info_transport_t, transport, _z_info_transport_check, _z_info_transport_null, _z_info_transport_copy, _z_info_transport_move, _z_info_transport_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_info_link_t, link, _z_info_link_check, _z_info_link_null, _z_info_link_copy, _z_info_link_move, _z_info_link_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_info_transport_event_t, transport_event, _z_info_transport_event_check, _z_info_transport_event_null, _z_info_transport_event_copy, _z_info_transport_event_move, _z_info_transport_event_clear) _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_info_link_event_t, link_event, _z_info_link_event_check, _z_info_link_event_null, _z_info_link_event_copy, _z_info_link_event_move, _z_info_link_event_clear) #endif _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_sample, _z_closure_sample_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_query, _z_closure_query_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_reply, _z_closure_reply_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_hello, z_closure_hello_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_zid, z_closure_zid_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_matching_status, _z_closure_matching_status_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL_PREFIX(ze, closure_miss, ze_closure_miss_callback_t, z_closure_drop_callback_t) #if Z_FEATURE_CONNECTIVITY == 1 _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_transport, z_closure_transport_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_link, z_closure_link_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_transport_event, z_closure_transport_event_callback_t, z_closure_drop_callback_t) _Z_OWNED_FUNCTIONS_CLOSURE_IMPL(closure_link_event, z_closure_link_event_callback_t, z_closure_drop_callback_t) #endif /************* Primitives **************/ #if Z_FEATURE_SCOUTING == 1 z_id_t z_hello_zid(const z_loaned_hello_t *hello) { return hello->_zid; } z_whatami_t z_hello_whatami(const z_loaned_hello_t *hello) { return hello->_whatami; } const z_loaned_string_array_t *zp_hello_locators(const z_loaned_hello_t *hello) { return &hello->_locators; } void z_hello_locators(const z_loaned_hello_t *hello, z_owned_string_array_t *locators_out) { z_string_array_clone(locators_out, &hello->_locators); } static const char *WHAT_AM_I_TO_STRING_MAP[8] = { "Other", // 0 "Router", // 0b1 "Peer", // 0b01 "Router|Peer", // 0b11, "Client", // 0b100 "Router|Client", // 0b101 "Peer|Client", // 0b110 "Router|Peer|Client" // 0b111 }; z_result_t z_whatami_to_view_string(z_whatami_t whatami, z_view_string_t *str_out) { uint8_t idx = (uint8_t)whatami; if (idx >= _ZP_ARRAY_SIZE(WHAT_AM_I_TO_STRING_MAP) || idx == 0) { z_view_string_from_str(str_out, WHAT_AM_I_TO_STRING_MAP[0]); _Z_ERROR_RETURN(_Z_ERR_INVALID); } else { z_view_string_from_str(str_out, WHAT_AM_I_TO_STRING_MAP[idx]); } return _Z_RES_OK; } typedef struct __z_hello_handler_wrapper_t { z_closure_hello_callback_t user_call; void *ctx; } __z_hello_handler_wrapper_t; void __z_hello_handler(_z_hello_t *hello, __z_hello_handler_wrapper_t *wrapped_ctx) { wrapped_ctx->user_call(hello, wrapped_ctx->ctx); } z_result_t z_scout(z_moved_config_t *config, z_moved_closure_hello_t *callback, const z_scout_options_t *options) { void *ctx = callback->_this._val.context; callback->_this._val.context = NULL; // TODO[API-NET]: When API and NET are a single layer, there is no wrap the user callback and args // to enclose the z_reply_t into a z_owned_reply_t. __z_hello_handler_wrapper_t *wrapped_ctx = (__z_hello_handler_wrapper_t *)z_malloc(sizeof(__z_hello_handler_wrapper_t)); if (wrapped_ctx == NULL) { z_internal_closure_hello_null(&callback->_this); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } wrapped_ctx->user_call = callback->_this._val.call; wrapped_ctx->ctx = ctx; z_what_t what; if (options != NULL) { what = options->what; } else { char *opt_as_str = _z_config_get(&config->_this._val, Z_CONFIG_SCOUTING_WHAT_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_SCOUTING_WHAT_DEFAULT; } what = strtol(opt_as_str, NULL, 10); } char *opt_as_str = _z_config_get(&config->_this._val, Z_CONFIG_MULTICAST_LOCATOR_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_MULTICAST_LOCATOR_DEFAULT; } _z_string_t mcast_locator = _z_string_alias_str(opt_as_str); uint32_t timeout; if (options != NULL) { timeout = options->timeout_ms; } else { opt_as_str = _z_config_get(&config->_this._val, Z_CONFIG_SCOUTING_TIMEOUT_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_SCOUTING_TIMEOUT_DEFAULT; } timeout = (uint32_t)strtoul(opt_as_str, NULL, 10); } _z_id_t zid = _z_id_empty(); char *zid_str = _z_config_get(&config->_this._val, Z_CONFIG_SESSION_ZID_KEY); if (zid_str != NULL) { _z_uuid_to_bytes(zid.id, zid_str); } _z_scout(what, zid, &mcast_locator, timeout, __z_hello_handler, wrapped_ctx, callback->_this._val.drop, ctx); z_free(wrapped_ctx); z_config_drop(config); z_internal_closure_hello_null(&callback->_this); return _Z_RES_OK; } #endif void z_open_options_default(z_open_options_t *options) { #if Z_FEATURE_ADMIN_SPACE == 1 options->auto_start_admin_space = false; #endif #if Z_FEATURE_MULTI_THREAD == 1 options->auto_start_lease_task = false; options->auto_start_read_task = true; options->executor_task_attributes = NULL; #endif #if Z_FEATURE_ADMIN_SPACE == 0 && Z_FEATURE_MULTI_THREAD == 0 options->__dummy = 0; #endif } static _z_id_t _z_session_get_zid(const _z_config_t *config) { _z_id_t zid = _z_id_empty(); char *opt_as_str = _z_config_get(config, Z_CONFIG_SESSION_ZID_KEY); if (opt_as_str != NULL) { _z_uuid_to_bytes(zid.id, opt_as_str); } else { _z_session_generate_zid(&zid, Z_ZID_LENGTH); } return zid; } static z_result_t _z_session_rc_init(z_owned_session_t *zs, _z_id_t *zid) { z_internal_session_null(zs); _z_session_t *s = (_z_session_t *)z_malloc(sizeof(_z_session_t)); if (s == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } z_result_t ret = _z_session_init(s, zid); if (ret != _Z_RES_OK) { _Z_ERROR("_z_open failed: %i", ret); z_free(s); return ret; } _z_session_rc_t zsrc = _z_session_rc_new(s); if (zsrc._cnt == NULL) { _z_session_clear(s); z_free(s); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } zs->_rc = zsrc; return _Z_RES_OK; } z_result_t z_open(z_owned_session_t *zs, z_moved_config_t *config, const z_open_options_t *options) { z_internal_session_null(zs); z_open_options_t opts; if (options == NULL) { z_open_options_default(&opts); } else { opts = *options; } if (config == NULL) { _Z_ERROR("A valid config is missing."); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_config_t *cfg = &config->_this._val; _z_id_t zid = _z_session_get_zid(cfg); if (!_z_id_check(zid)) { _Z_ERROR("Invalid ZID."); z_config_drop(config); return _Z_ERR_INVALID; } z_result_t ret = _z_session_rc_init(zs, &zid); if (ret != _Z_RES_OK) { z_config_drop(config); return ret; } ret = _z_open(&zs->_rc, cfg, &zid); // Move config ownership into the session before starting any background tasks, // as reconnect and async peer-add logic may access it. _Z_OWNED_RC_IN_VAL(zs)->_config = config->_this._val; z_internal_config_null(&config->_this); _Z_SET_IF_OK(ret, _zp_start_transport_tasks(_Z_RC_IN_VAL(&zs->_rc))); if (ret != _Z_RES_OK) { z_session_drop(z_session_move(zs)); return ret; } #if Z_FEATURE_MULTI_THREAD == 1 if (opts.auto_start_read_task) { _Z_CLEAN_RETURN_IF_ERR(_z_runtime_start(&_Z_RC_IN_VAL(&zs->_rc)->_runtime, opts.executor_task_attributes), z_session_drop(z_session_move(zs))); } #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADMIN_SPACE == 1 if (opts.auto_start_admin_space) { _Z_CLEAN_RETURN_IF_ERR(zp_start_admin_space(z_session_loan_mut(zs)), z_session_drop(z_session_move(zs))); } #endif #endif return _Z_RES_OK; } void z_close_options_default(z_close_options_t *options) { options->__dummy = 0; } void z_session_drop(z_moved_session_t *zs) { if (!_Z_RC_IS_NULL(&zs->_this._rc)) { // force closing of the session (since it might not do it automatically due to temporary pending rc copies) z_close(&zs->_this._rc, NULL); _z_session_rc_drop(&zs->_this._rc); } } z_result_t z_close(z_loaned_session_t *zs, const z_close_options_t *options) { _ZP_UNUSED(options); if (_Z_RC_IS_NULL(zs)) { return _Z_RES_OK; } return _z_session_close(_Z_RC_IN_VAL(zs)); } bool z_session_is_closed(const z_loaned_session_t *zs) { return _z_session_is_closed(_Z_RC_IN_VAL(zs)); } #ifdef Z_FEATURE_UNSTABLE_API z_entity_global_id_t z_session_id(const z_loaned_session_t *zs) { z_entity_global_id_t ret; // eid counter starts from 1, so it is safe to use 0 for session z_entity_global_id_new(&ret, &_Z_RC_IN_VAL(zs)->_local_zid, 0); // never fails return ret; } #endif z_result_t z_info_peers_zid(const z_loaned_session_t *zs, z_moved_closure_zid_t *callback) { if (_Z_RC_IN_VAL(zs)->_mode != Z_WHATAMI_PEER) { return _Z_RES_OK; } // Call transport function switch (_Z_RC_IN_VAL(zs)->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: _zp_unicast_fetch_zid(&(_Z_RC_IN_VAL(zs)->_tp), &callback->_this._val); break; case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: _zp_multicast_fetch_zid(&(_Z_RC_IN_VAL(zs)->_tp), &callback->_this._val); break; default: break; } z_closure_zid_drop(callback); return _Z_RES_OK; } z_result_t z_info_routers_zid(const z_loaned_session_t *zs, z_moved_closure_zid_t *callback) { if (_Z_RC_IN_VAL(zs)->_mode != Z_WHATAMI_CLIENT) { return _Z_RES_OK; } // Call transport function switch (_Z_RC_IN_VAL(zs)->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: _zp_unicast_fetch_zid(&(_Z_RC_IN_VAL(zs)->_tp), &callback->_this._val); break; default: break; } z_closure_zid_drop(callback); return _Z_RES_OK; } z_id_t z_info_zid(const z_loaned_session_t *zs) { return _Z_RC_IN_VAL(zs)->_local_zid; } #if Z_FEATURE_CONNECTIVITY == 1 void _z_info_transport_from_peer(_z_info_transport_t *out, const _z_transport_peer_common_t *peer, bool is_multicast) { *out = _z_info_transport_null(); out->_zid = peer->_remote_zid; out->_whatami = peer->_remote_whatami; out->_is_qos = false; out->_is_multicast = is_multicast; out->_is_shm = false; } bool _z_info_transport_filter_match(const _z_info_transport_t *transport, const _z_info_transport_t *filter) { return _z_id_eq(&transport->_zid, &filter->_zid) && transport->_is_multicast == filter->_is_multicast; } static z_result_t _z_info_link_make(_z_info_link_t *link, const _z_transport_peer_common_t *peer, const _z_transport_common_t *transport_common) { *link = _z_info_link_null(); link->_zid = peer->_remote_zid; if (transport_common != NULL && transport_common->_link != NULL) { link->_mtu = transport_common->_link->_mtu; link->_is_streamed = transport_common->_link->_cap._flow == Z_LINK_CAP_FLOW_STREAM; link->_is_reliable = transport_common->_link->_cap._is_reliable; } _Z_RETURN_IF_ERR(_z_string_copy(&link->_src, &peer->_link_src)); _Z_CLEAN_RETURN_IF_ERR(_z_string_copy(&link->_dst, &peer->_link_dst), _z_string_clear(&link->_src)); return _Z_RES_OK; } void z_info_links_options_default(z_info_links_options_t *options) { options->transport = NULL; } z_result_t z_info_transports(const z_loaned_session_t *zs, z_moved_closure_transport_t *callback) { z_result_t ret = _Z_RES_OK; _z_session_t *session = _Z_RC_IN_VAL(zs); _z_transport_t *transport = &session->_tp; _z_session_transport_mutex_lock(session); _z_transport_common_t *transport_common = _z_transport_get_common(transport); if (transport_common != NULL) { _z_transport_peer_mutex_lock(transport_common); } switch (transport->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_peer_unicast_slist_t *curr = transport->_transport._unicast._peers; for (; curr != NULL; curr = _z_transport_peer_unicast_slist_next(curr)) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(curr); _z_info_transport_t info_transport; _z_info_transport_from_peer(&info_transport, &peer->common, false); z_closure_transport_call(&callback->_this._val, &info_transport); } break; } case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_peer_multicast_slist_t *curr = transport->_transport._multicast._peers; for (; curr != NULL; curr = _z_transport_peer_multicast_slist_next(curr)) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(curr); _z_info_transport_t info_transport; _z_info_transport_from_peer(&info_transport, &peer->common, true); z_closure_transport_call(&callback->_this._val, &info_transport); } break; } default: break; } if (transport_common != NULL) { _z_transport_peer_mutex_unlock(transport_common); } _z_session_transport_mutex_unlock(session); z_closure_transport_drop(callback); return ret; } z_result_t z_info_links(const z_loaned_session_t *zs, z_moved_closure_link_t *callback, z_info_links_options_t *options) { z_result_t ret = _Z_RES_OK; z_info_links_options_t opt; z_info_links_options_default(&opt); if (options != NULL) { opt = *options; } _z_info_transport_t transport_filter = _z_info_transport_null(); bool has_transport_filter = false; if (opt.transport != NULL) { if (!z_internal_transport_check(&opt.transport->_this)) { ret = _Z_ERR_INVALID; } else { transport_filter = *z_transport_loan(&opt.transport->_this); has_transport_filter = true; } } _z_session_t *session = _Z_RC_IN_VAL(zs); _z_transport_t *transport = &session->_tp; if (ret != _Z_RES_OK) { goto out; } _z_session_transport_mutex_lock(session); _z_transport_common_t *transport_common = _z_transport_get_common(transport); if (transport_common != NULL) { _z_transport_peer_mutex_lock(transport_common); } switch (transport->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_peer_unicast_slist_t *curr = transport->_transport._unicast._peers; for (; curr != NULL; curr = _z_transport_peer_unicast_slist_next(curr)) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(curr); _z_info_transport_t info_transport; _z_info_transport_from_peer(&info_transport, &peer->common, false); if (has_transport_filter && !_z_info_transport_filter_match(&info_transport, &transport_filter)) { continue; } _z_info_link_t info_link; ret = _z_info_link_make(&info_link, &peer->common, transport_common); if (ret != _Z_RES_OK) { break; } z_closure_link_call(&callback->_this._val, &info_link); _z_info_link_clear(&info_link); } break; } case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_peer_multicast_slist_t *curr = transport->_transport._multicast._peers; for (; curr != NULL; curr = _z_transport_peer_multicast_slist_next(curr)) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(curr); _z_info_transport_t info_transport; _z_info_transport_from_peer(&info_transport, &peer->common, true); if (has_transport_filter && !_z_info_transport_filter_match(&info_transport, &transport_filter)) { continue; } _z_info_link_t info_link; ret = _z_info_link_make(&info_link, &peer->common, transport_common); if (ret != _Z_RES_OK) { break; } z_closure_link_call(&callback->_this._val, &info_link); _z_info_link_clear(&info_link); } break; } default: break; } if (transport_common != NULL) { _z_transport_peer_mutex_unlock(transport_common); } _z_session_transport_mutex_unlock(session); out: z_transport_drop(opt.transport); z_closure_link_drop(callback); return ret; } z_id_t z_transport_zid(const z_loaned_transport_t *transport) { return transport->_zid; } z_whatami_t z_transport_whatami(const z_loaned_transport_t *transport) { return transport->_whatami; } bool z_transport_is_qos(const z_loaned_transport_t *transport) { return transport->_is_qos; } bool z_transport_is_multicast(const z_loaned_transport_t *transport) { return transport->_is_multicast; } bool z_transport_is_shm(const z_loaned_transport_t *transport) { return transport->_is_shm; } z_id_t z_link_zid(const z_loaned_link_t *link) { return link->_zid; } z_result_t z_link_src(const z_loaned_link_t *link, z_owned_string_t *str_out) { str_out->_val = _z_string_null(); return _z_string_copy(&str_out->_val, &link->_src); } z_result_t z_link_dst(const z_loaned_link_t *link, z_owned_string_t *str_out) { str_out->_val = _z_string_null(); return _z_string_copy(&str_out->_val, &link->_dst); } uint16_t z_link_mtu(const z_loaned_link_t *link) { return link->_mtu; } bool z_link_is_streamed(const z_loaned_link_t *link) { return link->_is_streamed; } bool z_link_is_reliable(const z_loaned_link_t *link) { return link->_is_reliable; } void z_link_group(const z_loaned_link_t *link, z_owned_string_t *str_out) { (void)link; str_out->_val = _z_string_null(); } void z_link_auth_identifier(const z_loaned_link_t *link, z_owned_string_t *str_out) { (void)link; str_out->_val = _z_string_null(); } void z_link_interfaces(const z_loaned_link_t *link, z_owned_string_array_t *interfaces_out) { (void)link; interfaces_out->_val = _z_string_svec_null(); } bool z_link_priorities(const z_loaned_link_t *link, uint8_t *min_out, uint8_t *max_out) { (void)link; (void)min_out; (void)max_out; return false; } bool z_link_reliability(const z_loaned_link_t *link, z_reliability_t *reliability_out) { (void)link; (void)reliability_out; return false; } z_sample_kind_t z_transport_event_kind(const z_loaned_transport_event_t *event) { return event->kind; } const z_loaned_transport_t *z_transport_event_transport(const z_loaned_transport_event_t *event) { return &event->transport; } z_loaned_transport_t *z_transport_event_transport_mut(z_loaned_transport_event_t *event) { return &event->transport; } z_sample_kind_t z_link_event_kind(const z_loaned_link_event_t *event) { return event->kind; } const z_loaned_link_t *z_link_event_link(const z_loaned_link_event_t *event) { return &event->link; } z_loaned_link_t *z_link_event_link_mut(z_loaned_link_event_t *event) { return &event->link; } #endif z_result_t z_id_to_string(const z_id_t *id, z_owned_string_t *str) { str->_val = _z_id_to_string(id); if (!_z_string_check(&str->_val)) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } const z_loaned_keyexpr_t *z_sample_keyexpr(const z_loaned_sample_t *sample) { return &sample->keyexpr; } z_sample_kind_t z_sample_kind(const z_loaned_sample_t *sample) { return sample->kind; } #ifdef Z_FEATURE_UNSTABLE_API z_reliability_t z_sample_reliability(const z_loaned_sample_t *sample) { return sample->reliability; } const z_source_info_t *z_sample_source_info(const z_loaned_sample_t *sample) { const z_source_info_t *info = &sample->source_info; return _z_source_info_check(info) ? info : NULL; } #endif const z_loaned_bytes_t *z_sample_payload(const z_loaned_sample_t *sample) { return &sample->payload; } const z_timestamp_t *z_sample_timestamp(const z_loaned_sample_t *sample) { if (_z_timestamp_check(&sample->timestamp)) { return &sample->timestamp; } else { return NULL; } } const z_loaned_encoding_t *z_sample_encoding(const z_loaned_sample_t *sample) { return &sample->encoding; } const z_loaned_bytes_t *z_sample_attachment(const z_loaned_sample_t *sample) { return &sample->attachment; } z_congestion_control_t z_sample_congestion_control(const z_loaned_sample_t *sample) { return _z_n_qos_get_congestion_control(sample->qos); } bool z_sample_express(const z_loaned_sample_t *sample) { return _z_n_qos_get_express(sample->qos); } z_priority_t z_sample_priority(const z_loaned_sample_t *sample) { return _z_n_qos_get_priority(sample->qos); } const z_loaned_bytes_t *z_reply_err_payload(const z_loaned_reply_err_t *reply_err) { return &reply_err->payload; } const z_loaned_encoding_t *z_reply_err_encoding(const z_loaned_reply_err_t *reply_err) { return &reply_err->encoding; } const char *z_string_data(const z_loaned_string_t *str) { return _z_string_data(str); } size_t z_string_len(const z_loaned_string_t *str) { return _z_string_len(str); } z_result_t z_string_copy_from_str(z_owned_string_t *str, const char *value) { str->_val = _z_string_copy_from_str(value); if (str->_val._slice.start == NULL && value != NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } z_result_t z_string_from_str(z_owned_string_t *str, char *value, void (*deleter)(void *value, void *context), void *context) { str->_val = _z_string_from_str_custom_deleter(value, _z_delete_context_create(deleter, context)); return _Z_RES_OK; } void z_string_empty(z_owned_string_t *str) { str->_val = _z_string_null(); } z_result_t z_string_copy_from_substr(z_owned_string_t *str, const char *value, size_t len) { str->_val = _z_string_copy_from_substr(value, len); return _Z_RES_OK; } bool z_string_is_empty(const z_loaned_string_t *str) { return _z_string_is_empty(str); } const z_loaned_slice_t *z_string_as_slice(const z_loaned_string_t *str) { return &str->_slice; } z_priority_t z_priority_default(void) { return Z_PRIORITY_DEFAULT; } #if Z_FEATURE_PUBLICATION == 1 void _z_publisher_drop(_z_publisher_t *pub) { _z_undeclare_publisher(pub); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_publisher_t, publisher, _z_publisher_check, _z_publisher_null, _z_publisher_drop) void z_put_options_default(z_put_options_t *options) { options->congestion_control = z_internal_congestion_control_default_push(); options->priority = Z_PRIORITY_DEFAULT; options->encoding = NULL; options->is_express = false; options->timestamp = NULL; options->attachment = NULL; #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 options->allowed_destination = z_locality_default(); #endif #ifdef Z_FEATURE_UNSTABLE_API options->reliability = Z_RELIABILITY_DEFAULT; options->source_info = NULL; #endif } void z_delete_options_default(z_delete_options_t *options) { options->congestion_control = z_internal_congestion_control_default_push(); options->is_express = false; options->timestamp = NULL; options->priority = Z_PRIORITY_DEFAULT; #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 options->allowed_destination = z_locality_default(); #endif #ifdef Z_FEATURE_UNSTABLE_API options->reliability = Z_RELIABILITY_DEFAULT; options->source_info = NULL; #endif } z_result_t z_put(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_bytes_t *payload, const z_put_options_t *options) { z_result_t ret = 0; z_put_options_t opt; z_put_options_default(&opt); if (options != NULL) { opt = *options; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API reliability = opt.reliability; source_info = opt.source_info; #endif _z_bytes_t *payload_bytes = _z_bytes_from_moved(payload); _z_bytes_t *attachment_bytes = _z_bytes_from_moved(opt.attachment); _z_encoding_t *encoding = _z_encoding_from_moved(opt.encoding); z_locality_t allowed_destination = z_locality_default(); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 allowed_destination = opt.allowed_destination; #endif ret = _z_write(_Z_RC_IN_VAL(zs), keyexpr, payload_bytes, encoding, Z_SAMPLE_KIND_PUT, opt.congestion_control, opt.priority, opt.is_express, opt.timestamp, attachment_bytes, reliability, source_info, allowed_destination); z_encoding_drop(opt.encoding); z_bytes_drop(opt.attachment); z_bytes_drop(payload); return ret; } z_result_t z_delete(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const z_delete_options_t *options) { z_result_t ret = 0; z_delete_options_t opt; z_delete_options_default(&opt); if (options != NULL) { opt = *options; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API reliability = opt.reliability; source_info = opt.source_info; #endif z_locality_t allowed_destination = z_locality_default(); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 allowed_destination = opt.allowed_destination; #endif ret = _z_write(_Z_RC_IN_VAL(zs), keyexpr, NULL, NULL, Z_SAMPLE_KIND_DELETE, opt.congestion_control, opt.priority, opt.is_express, opt.timestamp, NULL, reliability, source_info, allowed_destination); return ret; } void z_publisher_options_default(z_publisher_options_t *options) { options->encoding = NULL; options->congestion_control = z_internal_congestion_control_default_push(); options->priority = Z_PRIORITY_DEFAULT; options->is_express = false; #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 options->allowed_destination = z_locality_default(); #endif #ifdef Z_FEATURE_UNSTABLE_API options->reliability = Z_RELIABILITY_DEFAULT; #endif } z_result_t z_declare_publisher(const z_loaned_session_t *zs, z_owned_publisher_t *pub, const z_loaned_keyexpr_t *keyexpr, const z_publisher_options_t *options) { pub->_val = _z_publisher_null(); // Set options z_publisher_options_t opt; z_publisher_options_default(&opt); if (options != NULL) { opt = *options; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; #ifdef Z_FEATURE_UNSTABLE_API reliability = opt.reliability; #endif z_locality_t allowed_destination = z_locality_default(); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 allowed_destination = opt.allowed_destination; #endif z_result_t res = _z_declare_publisher(&pub->_val, zs, keyexpr, opt.encoding == NULL ? NULL : &opt.encoding->_this._val, opt.congestion_control, opt.priority, opt.is_express, reliability, allowed_destination); _Z_SET_IF_OK(res, _z_write_filter_create(zs, &pub->_val._filter, &pub->_val._key, _Z_INTEREST_FLAG_SUBSCRIBERS, false, allowed_destination)); if (res != _Z_RES_OK) { _z_publisher_drop(&pub->_val); } return res; } z_result_t z_undeclare_publisher(z_moved_publisher_t *pub) { return _z_undeclare_publisher(&pub->_this._val); } void z_publisher_put_options_default(z_publisher_put_options_t *options) { options->encoding = NULL; options->attachment = NULL; options->timestamp = NULL; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; #endif } void z_publisher_delete_options_default(z_publisher_delete_options_t *options) { options->timestamp = NULL; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; #endif } #if Z_FEATURE_ADVANCED_PUBLICATION == 1 z_result_t _z_publisher_put_impl(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options, _ze_advanced_cache_t *cache) { #else z_result_t _z_publisher_put_impl(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options) { #endif z_result_t ret = 0; // Build options z_publisher_put_options_t opt; z_publisher_put_options_default(&opt); if (options != NULL) { opt = *options; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API reliability = pub->reliability; if (opt.source_info != NULL) { source_info = opt.source_info; } #endif _z_encoding_t encoding; if (opt.encoding == NULL) { encoding = _z_encoding_alias( &pub->_encoding); // it is safe to use alias, since it will be unaffected by clear operation } else { encoding = _z_encoding_steal(&opt.encoding->_this._val); } _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&pub->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { _Z_ERROR_LOG(_Z_ERR_SESSION_CLOSED); ret = _Z_ERR_SESSION_CLOSED; } #else session = _Z_RC_IN_VAL(&pub->_zn); #endif if (session != NULL) { _z_bytes_t *payload_bytes = _z_bytes_from_moved(payload); _z_bytes_t *attachment_bytes = _z_bytes_from_moved(opt.attachment); #if Z_FEATURE_ADVANCED_PUBLICATION == 1 if (cache != NULL) { _z_timestamp_t local_timestamp = (opt.timestamp != NULL) ? *opt.timestamp : _z_timestamp_null(); _z_source_info_t local_source_info = (source_info != NULL) ? *source_info : _z_source_info_null(); _z_bytes_t local_payload = (payload_bytes != NULL) ? *payload_bytes : _z_bytes_null(); _z_bytes_t local_attachment = (attachment_bytes != NULL) ? *attachment_bytes : _z_bytes_null(); _z_sample_t sample; z_result_t res = _z_sample_copy_data( &sample, &pub->_key, &local_payload, &local_timestamp, &encoding, Z_SAMPLE_KIND_PUT, _z_n_qos_make(pub->_is_express, pub->_congestion_control == Z_CONGESTION_CONTROL_BLOCK, pub->_priority), &local_attachment, reliability, &local_source_info); if (res == _Z_RES_OK) { res = _ze_advanced_cache_add(cache, &sample); if (res != _Z_RES_OK) { _Z_ERROR("Failed to add sample to advanced publisher cache: %i", res); _z_sample_clear(&sample); } } else { _Z_ERROR("Failed to create sample from data: %i", res); } } #endif // Check if write filter is active before writing if ( #if Z_FEATURE_MULTICAST_DECLARATIONS == 0 session->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE || #endif !_z_write_filter_active(&pub->_filter)) { // Write value ret = _z_write(session, &pub->_key, payload_bytes, &encoding, Z_SAMPLE_KIND_PUT, pub->_congestion_control, pub->_priority, pub->_is_express, opt.timestamp, attachment_bytes, reliability, source_info, pub->_allowed_destination); } } else { _Z_ERROR_LOG(_Z_ERR_SESSION_CLOSED); ret = _Z_ERR_SESSION_CLOSED; } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif // Clean-up _z_encoding_clear(&encoding); z_bytes_drop(opt.attachment); z_bytes_drop(payload); return ret; } z_result_t z_publisher_put(const z_loaned_publisher_t *pub, z_moved_bytes_t *payload, const z_publisher_put_options_t *options) { #if Z_FEATURE_ADVANCED_PUBLICATION == 1 return _z_publisher_put_impl(pub, payload, options, NULL); #else return _z_publisher_put_impl(pub, payload, options); #endif } #if Z_FEATURE_ADVANCED_PUBLICATION == 1 z_result_t _z_publisher_delete_impl(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options, _ze_advanced_cache_t *cache) { #else z_result_t _z_publisher_delete_impl(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options) { #endif // Build options z_publisher_delete_options_t opt; z_publisher_delete_options_default(&opt); if (options != NULL) { opt = *options; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API reliability = pub->reliability; source_info = opt.source_info; #endif _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&pub->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } #else session = _Z_RC_IN_VAL(&pub->_zn); #endif z_result_t ret = _Z_RES_OK; if ( #if Z_FEATURE_MULTICAST_DECLARATIONS == 0 session->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE || #endif !_z_write_filter_active(&pub->_filter)) { ret = _z_write(session, &pub->_key, NULL, NULL, Z_SAMPLE_KIND_DELETE, pub->_congestion_control, pub->_priority, pub->_is_express, opt.timestamp, NULL, reliability, source_info, pub->_allowed_destination); } #if Z_FEATURE_ADVANCED_PUBLICATION == 1 if (cache != NULL) { _z_timestamp_t cache_timestamp = (opt.timestamp != NULL) ? *opt.timestamp : _z_timestamp_null(); _z_source_info_t cache_source_info = (source_info != NULL) ? *source_info : _z_source_info_null(); _z_bytes_t payload_bytes = _z_bytes_null(); _z_bytes_t attachment_bytes = _z_bytes_null(); _z_sample_t sample; z_result_t res = _z_sample_copy_data( &sample, &pub->_key, &payload_bytes, &cache_timestamp, NULL, Z_SAMPLE_KIND_DELETE, _z_n_qos_make(pub->_is_express, pub->_congestion_control == Z_CONGESTION_CONTROL_BLOCK, pub->_priority), &attachment_bytes, reliability, &cache_source_info); if (res == _Z_RES_OK) { res = _ze_advanced_cache_add(cache, &sample); if (res != _Z_RES_OK) { _Z_ERROR("Failed to add sample to advanced publisher cache: %i", res); _z_sample_clear(&sample); } } else { _Z_ERROR("Failed to create sample from data: %i", res); } } #endif #if Z_FEATURE_SESSION_CHECK == 1 // Clean up _z_session_rc_drop(&sess_rc); #endif return ret; } z_result_t z_publisher_delete(const z_loaned_publisher_t *pub, const z_publisher_delete_options_t *options) { #if Z_FEATURE_ADVANCED_PUBLICATION == 1 return _z_publisher_delete_impl(pub, options, NULL); #else return _z_publisher_delete_impl(pub, options); #endif } const z_loaned_keyexpr_t *z_publisher_keyexpr(const z_loaned_publisher_t *publisher) { return (const z_loaned_keyexpr_t *)&publisher->_key; } #ifdef Z_FEATURE_UNSTABLE_API z_entity_global_id_t z_publisher_id(const z_loaned_publisher_t *publisher) { z_entity_global_id_t egid; _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&publisher->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { egid = _z_entity_global_id_null(); } #else session = _Z_RC_IN_VAL(&publisher->_zn); #endif if (session != NULL) { egid.zid = session->_local_zid; egid.eid = (uint32_t)publisher->_id; } else { egid = _z_entity_global_id_null(); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return egid; } #endif #if Z_FEATURE_MATCHING == 1 z_result_t z_publisher_declare_background_matching_listener(const z_loaned_publisher_t *publisher, z_moved_closure_matching_status_t *callback) { return z_publisher_declare_matching_listener(publisher, NULL, callback); } z_result_t z_publisher_declare_matching_listener(const z_loaned_publisher_t *publisher, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback) { _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&publisher->_zn); z_result_t ret = _Z_RES_OK; if (!_Z_RC_IS_NULL(&sess_rc)) { if (matching_listener != NULL) { matching_listener->_val = _z_matching_listener_null(); ret = _z_matching_listener_declare(&matching_listener->_val, _Z_RC_IN_VAL(&sess_rc), &publisher->_filter.ctx, &callback->_this._val); } else { uint32_t _listener_id; ret = _z_matching_listener_register(&_listener_id, _Z_RC_IN_VAL(&sess_rc), &publisher->_filter.ctx, &callback->_this._val); } _z_session_rc_drop(&sess_rc); } else { if (matching_listener != NULL) { matching_listener->_val = _z_matching_listener_null(); } _z_closure_matching_status_clear(&callback->_this._val); z_internal_closure_matching_status_null(&callback->_this); ret = _Z_ERR_GENERIC; } return ret; } z_result_t z_publisher_get_matching_status(const z_loaned_publisher_t *publisher, z_matching_status_t *matching_status) { matching_status->matching = !_z_write_filter_active(&publisher->_filter); return _Z_RES_OK; } #endif // Z_FEATURE_MATCHING == 1 #endif // Z_FEATURE_PUBLICATION == 1 #if Z_FEATURE_QUERY == 1 bool _z_reply_check(const _z_reply_t *reply) { if (reply->data._tag == _Z_REPLY_TAG_DATA) { return _z_sample_check(&reply->data._result.sample); } else if (reply->data._tag == _Z_REPLY_TAG_ERROR) { return _z_value_check(&reply->data._result.error); } return false; } _Z_OWNED_FUNCTIONS_VALUE_IMPL(_z_reply_t, reply, _z_reply_check, _z_reply_null, _z_reply_copy, _z_reply_move, _z_reply_clear) void z_get_options_default(z_get_options_t *options) { options->target = z_query_target_default(); options->consolidation = z_query_consolidation_default(); options->congestion_control = z_internal_congestion_control_default_request(); options->priority = Z_PRIORITY_DEFAULT; options->is_express = false; #if Z_FEATURE_LOCAL_QUERYABLE == 1 options->allowed_destination = z_locality_default(); #endif options->encoding = NULL; options->payload = NULL; options->attachment = NULL; options->timeout_ms = 0; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; options->cancellation_token = NULL; #endif options->accept_replies = z_reply_keyexpr_default(); } z_result_t z_get(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const char *parameters, z_moved_closure_reply_t *callback, z_get_options_t *options) { return z_get_with_parameters_substr(zs, keyexpr, parameters, parameters == NULL ? 0 : strlen(parameters), callback, options); } z_result_t z_get_with_parameters_substr(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const char *parameters, size_t parameters_len, z_moved_closure_reply_t *callback, z_get_options_t *options) { z_result_t ret = _Z_RES_OK; _z_closure_reply_t closure = callback->_this._val; z_internal_closure_reply_null(&callback->_this); z_get_options_t opt; if (options != NULL) { opt = *options; } else { z_get_options_default(&opt); } if (opt.timeout_ms == 0) { opt.timeout_ms = Z_GET_TIMEOUT_DEFAULT; } _z_source_info_t *source_info = NULL; _z_cancellation_token_rc_t *cancellation_token = NULL; #ifdef Z_FEATURE_UNSTABLE_API source_info = opt.source_info; cancellation_token = opt.cancellation_token == NULL ? NULL : &opt.cancellation_token->_this._rc; #endif _z_n_qos_t qos = _z_n_qos_make(opt.is_express, opt.congestion_control == Z_CONGESTION_CONTROL_BLOCK, opt.priority); z_locality_t allowed_destination = z_locality_default(); #if Z_FEATURE_LOCAL_QUERYABLE == 1 allowed_destination = opt.allowed_destination; #endif ret = _z_query(zs, _z_optional_id_make_none(), keyexpr, parameters, parameters_len, opt.target, opt.consolidation.mode, _z_bytes_from_moved(opt.payload), _z_encoding_from_moved(opt.encoding), closure.call, closure.drop, closure.context, opt.timeout_ms, _z_bytes_from_moved(opt.attachment), qos, source_info, opt.accept_replies, allowed_destination, cancellation_token); // Clean-up #ifdef Z_FEATURE_UNSTABLE_API z_cancellation_token_drop(opt.cancellation_token); #endif z_bytes_drop(opt.payload); z_encoding_drop(opt.encoding); z_bytes_drop(opt.attachment); return ret; } void _z_querier_drop(_z_querier_t *querier) { _z_undeclare_querier(querier); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_querier_t, querier, _z_querier_check, _z_querier_null, _z_querier_drop) void z_querier_get_options_default(z_querier_get_options_t *options) { options->encoding = NULL; options->attachment = NULL; options->payload = NULL; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; options->cancellation_token = NULL; #endif } void z_querier_options_default(z_querier_options_t *options) { options->encoding = NULL; options->target = z_query_target_default(); options->consolidation = z_query_consolidation_default(); options->congestion_control = z_internal_congestion_control_default_request(); options->priority = Z_PRIORITY_DEFAULT; options->is_express = false; #if Z_FEATURE_LOCAL_QUERYABLE == 1 options->allowed_destination = z_locality_default(); #endif options->timeout_ms = 0; options->accept_replies = z_reply_keyexpr_default(); } z_result_t z_declare_querier(const z_loaned_session_t *zs, z_owned_querier_t *querier, const z_loaned_keyexpr_t *keyexpr, z_querier_options_t *options) { querier->_val = _z_querier_null(); // Set options z_querier_options_t opt; if (options != NULL) { opt = *options; } else { z_querier_options_default(&opt); } if (opt.timeout_ms == 0) { opt.timeout_ms = Z_GET_TIMEOUT_DEFAULT; } z_reliability_t reliability = Z_RELIABILITY_DEFAULT; z_locality_t allowed_destination = z_locality_default(); #if Z_FEATURE_LOCAL_QUERYABLE == 1 allowed_destination = opt.allowed_destination; #endif z_result_t res = _z_declare_querier(&querier->_val, zs, keyexpr, opt.consolidation.mode, opt.congestion_control, opt.target, opt.priority, opt.is_express, opt.timeout_ms, opt.encoding == NULL ? NULL : &opt.encoding->_this._val, reliability, allowed_destination, opt.accept_replies); _Z_SET_IF_OK(res, _z_write_filter_create(zs, &querier->_val._filter, &querier->_val._key, _Z_INTEREST_FLAG_QUERYABLES, querier->_val._target == Z_QUERY_TARGET_ALL_COMPLETE, allowed_destination)); return res; } z_result_t z_undeclare_querier(z_moved_querier_t *querier) { return _z_undeclare_querier(&querier->_this._val); } z_result_t z_querier_get(const z_loaned_querier_t *querier, const char *parameters, z_moved_closure_reply_t *callback, z_querier_get_options_t *options) { return z_querier_get_with_parameters_substr(querier, parameters, parameters == NULL ? 0 : strlen(parameters), callback, options); } z_result_t z_querier_get_with_parameters_substr(const z_loaned_querier_t *querier, const char *parameters, size_t parameters_len, z_moved_closure_reply_t *callback, z_querier_get_options_t *options) { z_result_t ret = _Z_RES_OK; _z_closure_reply_t closure = callback->_this._val; z_internal_closure_reply_null(&callback->_this); z_querier_get_options_t opt; z_querier_get_options_default(&opt); if (options != NULL) { opt = *options; } _z_encoding_t encoding; if (opt.encoding == NULL) { encoding = _z_encoding_alias( &querier->_encoding); // it is safe to use alias, since it is unaffected by clear operation } else { encoding = _z_encoding_steal(&opt.encoding->_this._val); } _z_session_t *session = NULL; // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&querier->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { _Z_ERROR_LOG(_Z_ERR_SESSION_CLOSED); ret = _Z_ERR_SESSION_CLOSED; } _z_source_info_t *source_info = NULL; _z_cancellation_token_rc_t *cancellation_token = NULL; bool should_proceed = ret == _Z_RES_OK && !_z_write_filter_active(&querier->_filter); #if Z_FEATURE_MULTICAST_DECLARATIONS == 0 should_proceed = should_proceed || (session->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE); #endif #ifdef Z_FEATURE_UNSTABLE_API source_info = opt.source_info; cancellation_token = opt.cancellation_token == NULL ? NULL : &opt.cancellation_token->_this._rc; #endif if (should_proceed) { _z_n_qos_t qos = _z_n_qos_make(querier->_is_express, querier->_congestion_control == Z_CONGESTION_CONTROL_BLOCK, querier->_priority); ret = _z_query(&sess_rc, _z_optional_id_make_some(querier->_id), &querier->_key, parameters, parameters_len, querier->_target, querier->_consolidation_mode, _z_bytes_from_moved(opt.payload), &encoding, closure.call, closure.drop, closure.context, querier->_timeout_ms, _z_bytes_from_moved(opt.attachment), qos, source_info, querier->_accept_replies, querier->_allowed_destination, cancellation_token); } else if (closure.drop != NULL) { closure.drop(closure.context); } _z_session_rc_drop(&sess_rc); // Clean-up #ifdef Z_FEATURE_UNSTABLE_API z_cancellation_token_drop(opt.cancellation_token); #endif z_bytes_drop(opt.payload); _z_encoding_clear(&encoding); z_bytes_drop(opt.attachment); return ret; } const z_loaned_keyexpr_t *z_querier_keyexpr(const z_loaned_querier_t *querier) { return (const z_loaned_keyexpr_t *)&querier->_key; } #ifdef Z_FEATURE_UNSTABLE_API z_entity_global_id_t z_querier_id(const z_loaned_querier_t *querier) { z_entity_global_id_t egid; _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&querier->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { egid = _z_entity_global_id_null(); } #else session = _Z_RC_IN_VAL(&querier->_zn); #endif if (session != NULL) { egid.zid = session->_local_zid; egid.eid = (uint32_t)querier->_id; } else { egid = _z_entity_global_id_null(); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return egid; } #endif #if Z_FEATURE_MATCHING == 1 z_result_t z_querier_declare_background_matching_listener(const z_loaned_querier_t *querier, z_moved_closure_matching_status_t *callback) { return z_querier_declare_matching_listener(querier, NULL, callback); } z_result_t z_querier_declare_matching_listener(const z_loaned_querier_t *querier, z_owned_matching_listener_t *matching_listener, z_moved_closure_matching_status_t *callback) { _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&querier->_zn); z_result_t ret = _Z_RES_OK; if (!_Z_RC_IS_NULL(&sess_rc)) { if (matching_listener != NULL) { matching_listener->_val = _z_matching_listener_null(); ret = _z_matching_listener_declare(&matching_listener->_val, _Z_RC_IN_VAL(&sess_rc), &querier->_filter.ctx, &callback->_this._val); } else { uint32_t _listener_id; ret = _z_matching_listener_register(&_listener_id, _Z_RC_IN_VAL(&sess_rc), &querier->_filter.ctx, &callback->_this._val); } _z_session_rc_drop(&sess_rc); } else { if (matching_listener != NULL) { matching_listener->_val = _z_matching_listener_null(); } _z_closure_matching_status_clear(&callback->_this._val); z_internal_closure_matching_status_null(&callback->_this); ret = _Z_ERR_GENERIC; } return ret; } z_result_t z_querier_get_matching_status(const z_loaned_querier_t *querier, z_matching_status_t *matching_status) { matching_status->matching = !_z_write_filter_active(&querier->_filter); return _Z_RES_OK; } #endif // Z_FEATURE_MATCHING == 1 bool z_reply_is_ok(const z_loaned_reply_t *reply) { return reply->data._tag != _Z_REPLY_TAG_ERROR; } const z_loaned_sample_t *z_reply_ok(const z_loaned_reply_t *reply) { return &reply->data._result.sample; } const z_loaned_reply_err_t *z_reply_err(const z_loaned_reply_t *reply) { return &reply->data._result.error; } #ifdef Z_FEATURE_UNSTABLE_API bool z_reply_replier_id(const z_loaned_reply_t *reply, z_entity_global_id_t *out_id) { if (_z_entity_global_id_check(&reply->data.replier_id)) { *out_id = reply->data.replier_id; return true; } return false; } #endif // Z_FEATURE_UNSTABLE_API #endif // Z_FEATURE_QUERY == 1 #if Z_FEATURE_QUERYABLE == 1 _Z_OWNED_FUNCTIONS_RC_IMPL(query) z_result_t z_query_take_from_loaned(z_owned_query_t *dst, z_loaned_query_t *src) { dst->_rc = *src; _z_query_t q = _z_query_null(); *src = _z_query_rc_new_from_val(&q); if (_Z_RC_IS_NULL(src)) { *src = dst->_rc; // reset src to its original value z_internal_query_null(dst); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } void _z_queryable_drop(_z_queryable_t *queryable) { _z_undeclare_queryable(queryable); _z_queryable_clear(queryable); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_queryable_t, queryable, _z_queryable_check, _z_queryable_null, _z_queryable_drop) void z_queryable_options_default(z_queryable_options_t *options) { options->complete = _Z_QUERYABLE_COMPLETE_DEFAULT; #if Z_FEATURE_LOCAL_QUERYABLE == 1 options->allowed_origin = z_locality_default(); #endif } z_result_t z_declare_background_queryable(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_query_t *callback, const z_queryable_options_t *options) { return z_declare_queryable(zs, NULL, keyexpr, callback, options); } z_result_t z_declare_queryable(const z_loaned_session_t *zs, z_owned_queryable_t *queryable, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_query_t *callback, const z_queryable_options_t *options) { _z_closure_query_t closure = callback->_this._val; z_internal_closure_query_null(&callback->_this); z_queryable_options_t opt; z_queryable_options_default(&opt); if (options != NULL) { opt = *options; } z_locality_t allowed_origin = z_locality_default(); #if Z_FEATURE_LOCAL_QUERYABLE == 1 allowed_origin = opt.allowed_origin; #endif if (queryable != NULL) { return _z_declare_queryable(&queryable->_val, zs, keyexpr, opt.complete, closure.call, closure.drop, closure.context, allowed_origin); } else { uint32_t _queryable_id; return _z_register_queryable(&_queryable_id, zs, keyexpr, opt.complete, closure.call, closure.drop, closure.context, allowed_origin, NULL); } } z_result_t z_undeclare_queryable(z_moved_queryable_t *queryable) { z_result_t ret = _z_undeclare_queryable(&queryable->_this._val); _z_queryable_clear(&queryable->_this._val); return ret; } const z_loaned_keyexpr_t *z_queryable_keyexpr(const z_loaned_queryable_t *queryable) { // Retrieve keyexpr from session const z_loaned_keyexpr_t *ret = NULL; uint32_t lookup = queryable->_entity_id; #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&queryable->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { return ret; } _z_session_t *zn = _Z_RC_IN_VAL(&sess_rc); #else _z_session_t *zn = _z_session_weak_as_unsafe_ptr(&sub->_zn); #endif if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { _Z_WARN("Failed to lock session for queryable keyexpr retrieval - session may be closing"); #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } _z_session_queryable_rc_slist_t *node = zn->_local_queryable; while (node != NULL) { _z_session_queryable_rc_t *val = _z_session_queryable_rc_slist_value(node); if (_Z_RC_IN_VAL(val)->_id == lookup) { ret = (const z_loaned_keyexpr_t *)&_Z_RC_IN_VAL(val)->_key; break; } node = _z_session_queryable_rc_slist_next(node); } _z_session_mutex_unlock(zn); #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } void z_query_reply_options_default(z_query_reply_options_t *options) { options->encoding = NULL; options->congestion_control = Z_CONGESTION_CONTROL_BLOCK; options->priority = Z_PRIORITY_DEFAULT; options->timestamp = NULL; options->is_express = false; options->attachment = NULL; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; #endif } z_result_t z_query_reply(const z_loaned_query_t *query, const z_loaned_keyexpr_t *keyexpr, z_moved_bytes_t *payload, const z_query_reply_options_t *options) { // Try upgrading session weak to rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(query)->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } // Set options z_query_reply_options_t opts; if (options == NULL) { z_query_reply_options_default(&opts); } else { opts = *options; } _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API source_info = opts.source_info; #endif z_result_t ret = _z_send_reply(_Z_RC_IN_VAL(query), &sess_rc, keyexpr, _z_bytes_from_moved(payload), _z_encoding_from_moved(opts.encoding), Z_SAMPLE_KIND_PUT, opts.is_express, opts.timestamp, _z_bytes_from_moved(opts.attachment), source_info); // Clean-up _z_session_rc_drop(&sess_rc); z_encoding_drop(opts.encoding); z_bytes_drop(opts.attachment); z_bytes_drop(payload); return ret; } z_result_t _z_query_reply_sample(const z_loaned_query_t *query, z_loaned_sample_t *sample, const z_query_reply_options_t *options) { // Try upgrading session weak to rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(query)->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } // Set options z_query_reply_options_t opts; if (options == NULL) { z_query_reply_options_default(&opts); } else { opts = *options; } z_result_t ret = _z_send_reply(_Z_RC_IN_VAL(query), &sess_rc, &sample->keyexpr, &sample->payload, &sample->encoding, sample->kind, opts.is_express, &sample->timestamp, &sample->attachment, &sample->source_info); // Clean-up _z_session_rc_drop(&sess_rc); return ret; } void z_query_reply_del_options_default(z_query_reply_del_options_t *options) { options->congestion_control = Z_CONGESTION_CONTROL_BLOCK; options->priority = Z_PRIORITY_DEFAULT; options->timestamp = NULL; options->is_express = false; options->attachment = NULL; #ifdef Z_FEATURE_UNSTABLE_API options->source_info = NULL; #endif } z_result_t z_query_reply_del(const z_loaned_query_t *query, const z_loaned_keyexpr_t *keyexpr, const z_query_reply_del_options_t *options) { // Try upgrading session weak to rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(query)->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_query_reply_del_options_t opts; if (options == NULL) { z_query_reply_del_options_default(&opts); } else { opts = *options; } _z_source_info_t *source_info = NULL; #ifdef Z_FEATURE_UNSTABLE_API source_info = opts.source_info; #endif z_result_t ret = _z_send_reply(_Z_RC_IN_VAL(query), &sess_rc, keyexpr, NULL, NULL, Z_SAMPLE_KIND_DELETE, opts.is_express, opts.timestamp, _z_bytes_from_moved(opts.attachment), source_info); // Clean-up _z_session_rc_drop(&sess_rc); z_bytes_drop(opts.attachment); return ret; } void z_query_reply_err_options_default(z_query_reply_err_options_t *options) { options->encoding = NULL; } z_result_t z_query_reply_err(const z_loaned_query_t *query, z_moved_bytes_t *payload, const z_query_reply_err_options_t *options) { // Try upgrading session weak to rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(query)->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_query_reply_err_options_t opts; if (options == NULL) { z_query_reply_err_options_default(&opts); } else { opts = *options; } z_result_t ret = _z_send_reply_err(_Z_RC_IN_VAL(query), &sess_rc, _z_bytes_from_moved(payload), _z_encoding_from_moved(opts.encoding)); // Clean-up _z_session_rc_drop(&sess_rc); z_encoding_drop(opts.encoding); z_bytes_drop(payload); return ret; } #ifdef Z_FEATURE_UNSTABLE_API z_entity_global_id_t z_queryable_id(const z_loaned_queryable_t *queryable) { z_entity_global_id_t egid; _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&queryable->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { egid = _z_entity_global_id_null(); } #else session = _Z_RC_IN_VAL(&queryable->_zn); #endif if (session != NULL) { egid.zid = session->_local_zid; egid.eid = queryable->_entity_id; } else { egid = _z_entity_global_id_null(); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return egid; } #endif #endif z_result_t z_keyexpr_from_str_autocanonize(z_owned_keyexpr_t *key, const char *name) { size_t len = strlen(name); return z_keyexpr_from_substr_autocanonize(key, name, &len); } z_result_t z_keyexpr_from_substr_autocanonize(z_owned_keyexpr_t *key, const char *name, size_t *len) { _Z_RETURN_IF_ERR(z_keyexpr_from_substr(key, name, *len)); _Z_CLEAN_RETURN_IF_ERR( z_keyexpr_canonize((char *)_z_string_data(&key->_val._inner._keyexpr), &key->_val._inner._keyexpr._slice.len), _z_declared_keyexpr_clear(&key->_val)); *len = _z_string_len(&key->_val._inner._keyexpr); return _Z_RES_OK; } z_result_t z_keyexpr_from_str(z_owned_keyexpr_t *key, const char *name) { return z_keyexpr_from_substr(key, name, strlen(name)); } z_result_t z_keyexpr_from_substr(z_owned_keyexpr_t *key, const char *name, size_t len) { z_internal_keyexpr_null(key); return _z_declared_keyexpr_from_substr(&key->_val, name, len); } z_result_t z_declare_keyexpr(const z_loaned_session_t *zs, z_owned_keyexpr_t *key, const z_loaned_keyexpr_t *keyexpr) { #if Z_FEATURE_MULTICAST_DECLARATIONS == 0 if (_Z_RC_IN_VAL(zs)->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) { _Z_WARN( "Declaring a keyexpr without Z_FEATURE_MULTICAST_DECLARATIONS might generate unknown key expression errors " "during communications\n"); } #endif return _z_declared_keyexpr_declare(zs, &key->_val, keyexpr); } z_result_t z_undeclare_keyexpr(const z_loaned_session_t *zs, z_moved_keyexpr_t *keyexpr) { _z_keyexpr_wire_declaration_rc_t *declaration = &keyexpr->_this._val._declaration; z_result_t ret = _Z_RES_OK; if (_Z_RC_IS_NULL(declaration)) { ret = _Z_ERR_INVALID; } else if (!_z_keyexpr_wire_declaration_is_declared_on_session(_Z_RC_IN_VAL(declaration), _Z_RC_IN_VAL(zs))) { ret = _Z_ERR_KEYEXPR_DECLARED_ON_ANOTHER_SESSION; } else if (_z_keyexpr_wire_declaration_rc_strong_count(declaration) == 1) { ret = _z_keyexpr_wire_declaration_undeclare(_Z_RC_IN_VAL(declaration)); } z_keyexpr_drop(keyexpr); return ret; } #if Z_FEATURE_SUBSCRIPTION == 1 void _z_subscriber_drop(_z_subscriber_t *sub) { _z_undeclare_subscriber(sub); _z_subscriber_clear(sub); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_subscriber_t, subscriber, _z_subscriber_check, _z_subscriber_null, _z_subscriber_drop) void z_subscriber_options_default(z_subscriber_options_t *options) { #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 options->allowed_origin = z_locality_default(); #else options->__dummy = 0; #endif } z_result_t z_declare_background_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, const z_subscriber_options_t *options) { return z_declare_subscriber(zs, NULL, keyexpr, callback, options); } z_result_t z_declare_subscriber(const z_loaned_session_t *zs, z_owned_subscriber_t *sub, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, const z_subscriber_options_t *options) { _z_closure_sample_t closure = callback->_this._val; z_internal_closure_sample_null(&callback->_this); z_subscriber_options_t opt; z_subscriber_options_default(&opt); if (options != NULL) { opt = *options; } z_locality_t allowed_origin = z_locality_default(); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 allowed_origin = opt.allowed_origin; #endif if (sub != NULL) { sub->_val = _z_subscriber_null(); return _z_declare_subscriber(&sub->_val, zs, keyexpr, closure.call, closure.drop, closure.context, allowed_origin); } else { uint32_t _sub_id; return _z_register_subscriber(&_sub_id, zs, keyexpr, closure.call, closure.drop, closure.context, allowed_origin, NULL); } } z_result_t z_undeclare_subscriber(z_moved_subscriber_t *sub) { z_result_t ret = _z_undeclare_subscriber(&sub->_this._val); _z_subscriber_clear(&sub->_this._val); return ret; } const z_loaned_keyexpr_t *z_subscriber_keyexpr(const z_loaned_subscriber_t *sub) { const z_loaned_keyexpr_t *ret = NULL; // Retrieve keyexpr from session uint32_t lookup = sub->_entity_id; #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&sub->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { return ret; } _z_session_t *zn = _Z_RC_IN_VAL(&sess_rc); #else _z_session_t *zn = _z_session_weak_as_unsafe_ptr(&sub->_zn); #endif if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { _Z_WARN("Failed to lock session for subscriber keyexpr retrieval - session may be closing"); #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } _z_subscription_rc_slist_t *node = zn->_subscriptions; while (node != NULL) { _z_subscription_rc_t *val = _z_subscription_rc_slist_value(node); if (_Z_RC_IN_VAL(val)->_id == lookup) { ret = (const z_loaned_keyexpr_t *)&_Z_RC_IN_VAL(val)->_key; break; } node = _z_subscription_rc_slist_next(node); } _z_session_mutex_unlock(zn); #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } #ifdef Z_FEATURE_UNSTABLE_API z_entity_global_id_t z_subscriber_id(const z_loaned_subscriber_t *subscriber) { z_entity_global_id_t egid; _z_session_t *session = NULL; #if Z_FEATURE_SESSION_CHECK == 1 // Try to upgrade session rc _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&subscriber->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { session = _Z_RC_IN_VAL(&sess_rc); } else { egid = _z_entity_global_id_null(); } #else session = _Z_RC_IN_VAL(&subscriber->_zn); #endif if (session != NULL) { egid.zid = session->_local_zid; egid.eid = subscriber->_entity_id; } else { egid = _z_entity_global_id_null(); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return egid; } #endif #endif #if Z_FEATURE_BATCHING == 1 z_result_t zp_batch_start(const z_loaned_session_t *zs) { if (_Z_RC_IS_NULL(zs)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } _z_session_t *session = _Z_RC_IN_VAL(zs); return _z_transport_start_batching(&session->_tp); } z_result_t zp_batch_flush(const z_loaned_session_t *zs) { _z_session_t *session = _Z_RC_IN_VAL(zs); if (_Z_RC_IS_NULL(zs)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } // Send current batch without dropping return _z_send_n_batch(session, Z_CONGESTION_CONTROL_BLOCK); } z_result_t zp_batch_stop(const z_loaned_session_t *zs) { _z_session_t *session = _Z_RC_IN_VAL(zs); if (_Z_RC_IS_NULL(zs)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } _Z_RETURN_IF_ERR(_z_transport_stop_batching(&session->_tp)); // Send remaining batch without dropping return _z_send_n_batch(session, Z_CONGESTION_CONTROL_BLOCK); } #endif #if Z_FEATURE_MATCHING == 1 void _z_matching_listener_drop(_z_matching_listener_t *listener) { _z_matching_listener_undeclare(listener); _z_matching_listener_clear(listener); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_matching_listener_t, matching_listener, _z_matching_listener_check, _z_matching_listener_null, _z_matching_listener_drop) z_result_t z_undeclare_matching_listener(z_moved_matching_listener_t *listener) { z_result_t ret = _z_matching_listener_undeclare(&listener->_this._val); _z_matching_listener_clear(&listener->_this._val); return ret; } #endif #if Z_FEATURE_MULTI_THREAD == 1 /**************** Tasks ****************/ void zp_task_read_options_default(zp_task_read_options_t *options) { options->task_attributes = NULL; } z_result_t zp_start_read_task(z_loaned_session_t *zs, const zp_task_read_options_t *options) { return _z_background_executor_start(&_Z_RC_IN_VAL(zs)->_runtime, options == NULL ? NULL : options->task_attributes); } z_result_t zp_stop_read_task(z_loaned_session_t *zs) { return _z_runtime_stop((_z_runtime_t *)&_Z_RC_IN_VAL(zs)->_runtime); } bool zp_read_task_is_running(const z_loaned_session_t *zs) { return _z_background_executor_is_running(&_Z_RC_IN_VAL(zs)->_runtime); } void zp_task_lease_options_default(zp_task_lease_options_t *options) { options->task_attributes = NULL; } z_result_t zp_start_lease_task(z_loaned_session_t *zs, const zp_task_lease_options_t *options) { _ZP_UNUSED(zs); _ZP_UNUSED(options); return _Z_RES_OK; } z_result_t zp_stop_lease_task(z_loaned_session_t *zs) { _ZP_UNUSED(zs); return _Z_RES_OK; } bool zp_lease_task_is_running(const z_loaned_session_t *zs) { return _z_background_executor_is_running(&_Z_RC_IN_VAL(zs)->_runtime); } #else void zp_read_options_default(zp_read_options_t *options) { options->single_read = false; } z_result_t zp_read(const z_loaned_session_t *zs, const zp_read_options_t *options) { zp_read_options_t opt; if (options != NULL) { opt = *options; } else { zp_read_options_default(&opt); } return _zp_read(_Z_RC_IN_VAL(zs), opt.single_read); } void zp_send_keep_alive_options_default(zp_send_keep_alive_options_t *options) { options->__dummy = 0; } z_result_t zp_send_keep_alive(const z_loaned_session_t *zs, const zp_send_keep_alive_options_t *options) { (void)(options); return _zp_send_keep_alive(_Z_RC_IN_VAL(zs)); } void zp_send_join_options_default(zp_send_join_options_t *options) { options->__dummy = 0; } z_result_t zp_send_join(const z_loaned_session_t *zs, const zp_send_join_options_t *options) { (void)(options); return _zp_send_join(_Z_RC_IN_VAL(zs)); } void zp_spin_once(const z_loaned_session_t *zs) { if (!z_session_is_closed(zs)) { _z_runtime_spin_once((_z_runtime_t *)&_Z_RC_IN_VAL(zs)->_runtime); } } #endif #ifdef Z_FEATURE_UNSTABLE_API z_reliability_t z_reliability_default(void) { return Z_RELIABILITY_DEFAULT; } #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_QUERY == 1 _Z_OWNED_FUNCTIONS_RC_IMPL(cancellation_token) z_result_t z_cancellation_token_new(z_owned_cancellation_token_t *cancellation_token) { _z_cancellation_token_t *ct = (_z_cancellation_token_t *)z_malloc(sizeof(_z_cancellation_token_t)); if (ct == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _Z_CLEAN_RETURN_IF_ERR(_z_cancellation_token_create(ct), z_free(ct)); cancellation_token->_rc = _z_cancellation_token_rc_new(ct); if (_Z_RC_IS_NULL(&cancellation_token->_rc)) { _z_cancellation_token_clear(ct); z_free(ct); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return _Z_RES_OK; } z_result_t z_cancellation_token_cancel(z_loaned_cancellation_token_t *cancellation_token) { return _z_cancellation_token_cancel(_Z_RC_IN_VAL(cancellation_token)); } bool z_cancellation_token_is_cancelled(const z_loaned_cancellation_token_t *cancellation_token) { return _z_cancellation_token_is_cancelled(_Z_RC_IN_VAL(cancellation_token)); } #endif #endif ================================================ FILE: src/api/connectivity.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_CONNECTIVITY == 1 static inline void _z_connectivity_transport_from_event_data(_z_info_transport_t *out, const _z_connectivity_peer_event_data_t *peer, bool is_multicast) { *out = (_z_info_transport_t){0}; out->_zid = peer->_remote_zid; out->_whatami = peer->_remote_whatami; out->_is_qos = false; out->_is_multicast = is_multicast; out->_is_shm = false; } static z_result_t _z_connectivity_link_fill(_z_info_link_t *link, const _z_connectivity_peer_event_data_t *peer, uint16_t mtu, bool is_streamed, bool is_reliable) { *link = (_z_info_link_t){0}; link->_src = _z_string_null(); link->_dst = _z_string_null(); link->_zid = peer->_remote_zid; link->_mtu = mtu; link->_is_streamed = is_streamed; link->_is_reliable = is_reliable; if (_z_string_check(&peer->_link_src)) { _Z_RETURN_IF_ERR(_z_string_copy(&link->_src, &peer->_link_src)); } if (_z_string_check(&peer->_link_dst)) { _Z_CLEAN_RETURN_IF_ERR(_z_string_copy(&link->_dst, &peer->_link_dst), _z_string_clear(&link->_src)); } return _Z_RES_OK; } static inline void _z_connectivity_link_clear(_z_info_link_t *link) { _z_string_clear(&link->_src); _z_string_clear(&link->_dst); *link = (_z_info_link_t){0}; } static inline void _z_connectivity_link_event_clear(_z_info_link_event_t *event) { _z_connectivity_link_clear(&event->link); event->kind = Z_SAMPLE_KIND_DEFAULT; } static bool _z_connectivity_dispatch_link_put_for_peer(_z_closure_link_event_t *callback, const _z_transport_common_t *transport_common, const _z_transport_peer_common_t *peer, bool is_multicast, bool has_transport_filter, const _z_info_transport_t *transport_filter) { _z_connectivity_peer_event_data_t peer_event_data = {0}; _z_connectivity_peer_event_data_alias_from_common(&peer_event_data, peer); _z_info_transport_t info_transport; _z_connectivity_transport_from_event_data(&info_transport, &peer_event_data, is_multicast); if (has_transport_filter && !_z_info_transport_filter_match(&info_transport, transport_filter)) { return true; } uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_link_properties_from_transport(transport_common, &mtu, &is_streamed, &is_reliable); _z_info_link_event_t event = {0}; event.kind = Z_SAMPLE_KIND_PUT; if (_z_connectivity_link_fill(&event.link, &peer_event_data, mtu, is_streamed, is_reliable) != _Z_RES_OK) { _z_connectivity_link_event_clear(&event); return false; } callback->call(&event, callback->context); _z_connectivity_link_event_clear(&event); return true; } typedef struct { _z_closure_transport_event_t _closure; _z_sync_group_notifier_t _session_callback_drop_notifier; _z_sync_group_notifier_t _listener_callback_drop_notifier; } _z_connectivity_transport_cb_state_t; typedef struct { _z_closure_link_event_t _closure; _z_sync_group_notifier_t _session_callback_drop_notifier; _z_sync_group_notifier_t _listener_callback_drop_notifier; } _z_connectivity_link_cb_state_t; static void _z_connectivity_transport_cb_state_clear(_z_connectivity_transport_cb_state_t *state) { if (state == NULL) { return; } if (state->_closure.drop != NULL) { state->_closure.drop(state->_closure.context); } state->_closure.call = NULL; state->_closure.drop = NULL; state->_closure.context = NULL; _z_sync_group_notifier_drop(&state->_session_callback_drop_notifier); _z_sync_group_notifier_drop(&state->_listener_callback_drop_notifier); } static void _z_connectivity_link_cb_state_clear(_z_connectivity_link_cb_state_t *state) { if (state == NULL) { return; } if (state->_closure.drop != NULL) { state->_closure.drop(state->_closure.context); } state->_closure.call = NULL; state->_closure.drop = NULL; state->_closure.context = NULL; _z_sync_group_notifier_drop(&state->_session_callback_drop_notifier); _z_sync_group_notifier_drop(&state->_listener_callback_drop_notifier); } static void _z_connectivity_transport_event_callback_drop(void *callback) { _z_connectivity_transport_cb_state_t *state = (_z_connectivity_transport_cb_state_t *)callback; _z_connectivity_transport_cb_state_clear(state); } static void _z_connectivity_link_event_callback_drop(void *callback) { _z_connectivity_link_cb_state_t *state = (_z_connectivity_link_cb_state_t *)callback; _z_connectivity_link_cb_state_clear(state); } static z_result_t _z_connectivity_take_transport_callback( _z_void_rc_t *out, z_moved_closure_transport_event_t *callback, const _z_sync_group_t *session_callback_drop_sync_group, const _z_sync_group_t *opt_listener_callback_drop_sync_group) { *out = _z_void_rc_null(); _z_closure_transport_event_t closure = callback->_this._val; z_internal_closure_transport_event_null(&callback->_this); _z_connectivity_transport_cb_state_t *stored = (_z_connectivity_transport_cb_state_t *)z_malloc(sizeof(_z_connectivity_transport_cb_state_t)); if (stored == NULL) { if (closure.drop != NULL) { closure.drop(closure.context); } _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *stored = (_z_connectivity_transport_cb_state_t){ ._closure = closure, ._session_callback_drop_notifier = _z_sync_group_notifier_null(), ._listener_callback_drop_notifier = _z_sync_group_notifier_null(), }; z_result_t ret = _z_sync_group_create_notifier(session_callback_drop_sync_group, &stored->_session_callback_drop_notifier); if (ret == _Z_RES_OK && opt_listener_callback_drop_sync_group != NULL && _z_sync_group_check(opt_listener_callback_drop_sync_group)) { ret = _z_sync_group_create_notifier(opt_listener_callback_drop_sync_group, &stored->_listener_callback_drop_notifier); } if (ret != _Z_RES_OK) { _z_connectivity_transport_cb_state_clear(stored); z_free(stored); return ret; } *out = _z_void_rc_rc_new(stored, _z_connectivity_transport_event_callback_drop); if (_Z_RC_IS_NULL(out)) { _z_connectivity_transport_cb_state_clear(stored); z_free(stored); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } static z_result_t _z_connectivity_take_link_callback(_z_void_rc_t *out, z_moved_closure_link_event_t *callback, const _z_sync_group_t *session_callback_drop_sync_group, const _z_sync_group_t *opt_listener_callback_drop_sync_group) { *out = _z_void_rc_null(); _z_closure_link_event_t closure = callback->_this._val; z_internal_closure_link_event_null(&callback->_this); _z_connectivity_link_cb_state_t *stored = (_z_connectivity_link_cb_state_t *)z_malloc(sizeof(_z_connectivity_link_cb_state_t)); if (stored == NULL) { if (closure.drop != NULL) { closure.drop(closure.context); } _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *stored = (_z_connectivity_link_cb_state_t){ ._closure = closure, ._session_callback_drop_notifier = _z_sync_group_notifier_null(), ._listener_callback_drop_notifier = _z_sync_group_notifier_null(), }; z_result_t ret = _z_sync_group_create_notifier(session_callback_drop_sync_group, &stored->_session_callback_drop_notifier); if (ret == _Z_RES_OK && opt_listener_callback_drop_sync_group != NULL && _z_sync_group_check(opt_listener_callback_drop_sync_group)) { ret = _z_sync_group_create_notifier(opt_listener_callback_drop_sync_group, &stored->_listener_callback_drop_notifier); } if (ret != _Z_RES_OK) { _z_connectivity_link_cb_state_clear(stored); z_free(stored); return ret; } *out = _z_void_rc_rc_new(stored, _z_connectivity_link_event_callback_drop); if (_Z_RC_IS_NULL(out)) { _z_connectivity_link_cb_state_clear(stored); z_free(stored); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } static inline _z_connectivity_transport_cb_state_t *_z_connectivity_transport_listener_state( const _z_connectivity_transport_listener_t *listener) { return (_z_connectivity_transport_cb_state_t *)listener->_callback._val; } static inline _z_connectivity_link_cb_state_t *_z_connectivity_link_listener_state( const _z_connectivity_link_listener_t *listener) { return (_z_connectivity_link_cb_state_t *)listener->_callback._val; } static inline _z_closure_transport_event_t *_z_connectivity_transport_listener_callback( const _z_connectivity_transport_listener_t *listener) { _z_connectivity_transport_cb_state_t *state = _z_connectivity_transport_listener_state(listener); return state != NULL ? &state->_closure : NULL; } static inline _z_closure_link_event_t *_z_connectivity_link_listener_callback( const _z_connectivity_link_listener_t *listener) { _z_connectivity_link_cb_state_t *state = _z_connectivity_link_listener_state(listener); return state != NULL ? &state->_closure : NULL; } static void _z_connectivity_dispatch_transport_event(_z_session_t *session, _z_info_transport_event_t *event) { _z_connectivity_transport_listener_intmap_t snapshot = _z_connectivity_transport_listener_intmap_make(); _z_session_mutex_lock(session); snapshot = _z_connectivity_transport_listener_intmap_clone(&session->_connectivity_transport_event_listeners); _z_session_mutex_unlock(session); _z_connectivity_transport_listener_intmap_iterator_t it = _z_connectivity_transport_listener_intmap_iterator_make(&snapshot); while (_z_connectivity_transport_listener_intmap_iterator_next(&it)) { _z_connectivity_transport_listener_t *listener = _z_connectivity_transport_listener_intmap_iterator_value(&it); _z_closure_transport_event_t *closure = _z_connectivity_transport_listener_callback(listener); if (closure != NULL && closure->call != NULL) { closure->call(event, closure->context); } } _z_connectivity_transport_listener_intmap_clear(&snapshot); } static void _z_connectivity_dispatch_link_event(_z_session_t *session, _z_info_link_event_t *event, bool is_multicast) { _z_connectivity_link_listener_intmap_t snapshot = _z_connectivity_link_listener_intmap_make(); _z_session_mutex_lock(session); snapshot = _z_connectivity_link_listener_intmap_clone(&session->_connectivity_link_event_listeners); _z_session_mutex_unlock(session); _z_connectivity_link_listener_intmap_iterator_t it = _z_connectivity_link_listener_intmap_iterator_make(&snapshot); while (_z_connectivity_link_listener_intmap_iterator_next(&it)) { _z_connectivity_link_listener_t *listener = _z_connectivity_link_listener_intmap_iterator_value(&it); if (listener->_has_transport_filter && (!_z_id_eq(&listener->_transport_zid, &event->link._zid) || listener->_transport_is_multicast != is_multicast)) { continue; } _z_closure_link_event_t *closure = _z_connectivity_link_listener_callback(listener); if (closure != NULL && closure->call != NULL) { closure->call(event, closure->context); } } _z_connectivity_link_listener_intmap_clear(&snapshot); } static void _z_connectivity_replay_transport_history(_z_session_t *session, _z_connectivity_transport_cb_state_t *callback_state) { if (callback_state == NULL || callback_state->_closure.call == NULL) { return; } _z_closure_transport_event_t *callback = &callback_state->_closure; _z_transport_t *transport = &session->_tp; _z_transport_common_t *transport_common = _z_transport_get_common(transport); if (transport_common != NULL) { _z_transport_peer_mutex_lock(transport_common); } switch (transport->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_peer_unicast_slist_t *curr = transport->_transport._unicast._peers; for (; curr != NULL; curr = _z_transport_peer_unicast_slist_next(curr)) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(curr); _z_info_transport_event_t event = {0}; event.kind = Z_SAMPLE_KIND_PUT; _z_info_transport_from_peer(&event.transport, &peer->common, false); callback->call(&event, callback->context); } break; } case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_peer_multicast_slist_t *curr = transport->_transport._multicast._peers; for (; curr != NULL; curr = _z_transport_peer_multicast_slist_next(curr)) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(curr); _z_info_transport_event_t event = {0}; event.kind = Z_SAMPLE_KIND_PUT; _z_info_transport_from_peer(&event.transport, &peer->common, true); callback->call(&event, callback->context); } break; } default: break; } if (transport_common != NULL) { _z_transport_peer_mutex_unlock(transport_common); } } static void _z_connectivity_replay_link_history(_z_session_t *session, _z_connectivity_link_cb_state_t *callback_state, bool has_transport_filter, const _z_info_transport_t *transport_filter) { if (callback_state == NULL || callback_state->_closure.call == NULL) { return; } _z_closure_link_event_t *callback = &callback_state->_closure; _z_transport_t *transport = &session->_tp; _z_transport_common_t *transport_common = _z_transport_get_common(transport); if (transport_common != NULL) { _z_transport_peer_mutex_lock(transport_common); } switch (transport->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_peer_unicast_slist_t *curr = transport->_transport._unicast._peers; for (; curr != NULL; curr = _z_transport_peer_unicast_slist_next(curr)) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(curr); if (!_z_connectivity_dispatch_link_put_for_peer(callback, transport_common, &peer->common, false, has_transport_filter, transport_filter)) { break; } } break; } case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_peer_multicast_slist_t *curr = transport->_transport._multicast._peers; for (; curr != NULL; curr = _z_transport_peer_multicast_slist_next(curr)) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(curr); if (!_z_connectivity_dispatch_link_put_for_peer(callback, transport_common, &peer->common, true, has_transport_filter, transport_filter)) { break; } } break; } default: break; } if (transport_common != NULL) { _z_transport_peer_mutex_unlock(transport_common); } } bool _z_transport_events_listener_check(const _z_transport_events_listener_t *listener) { return !_Z_RC_IS_NULL(&listener->_session); } _z_transport_events_listener_t _z_transport_events_listener_null(void) { return (_z_transport_events_listener_t){0}; } void _z_transport_events_listener_clear(_z_transport_events_listener_t *listener) { _z_session_weak_drop(&listener->_session); _z_sync_group_drop(&listener->_callback_drop_sync_group); *listener = _z_transport_events_listener_null(); } static z_result_t _z_transport_events_listener_undeclare(_z_transport_events_listener_t *listener) { _z_session_rc_t session_rc = _z_session_weak_upgrade(&listener->_session); if (!_Z_RC_IS_NULL(&session_rc)) { _z_session_t *session = _Z_RC_IN_VAL(&session_rc); _z_session_mutex_lock(session); _z_connectivity_transport_listener_intmap_remove(&session->_connectivity_transport_event_listeners, listener->_id); _z_session_mutex_unlock(session); _z_session_rc_drop(&session_rc); } if (_z_sync_group_check(&listener->_callback_drop_sync_group)) { return _z_sync_group_wait(&listener->_callback_drop_sync_group); } return _Z_RES_OK; } void _z_transport_events_listener_drop(_z_transport_events_listener_t *listener) { _z_transport_events_listener_undeclare(listener); _z_transport_events_listener_clear(listener); } bool _z_link_events_listener_check(const _z_link_events_listener_t *listener) { return !_Z_RC_IS_NULL(&listener->_session); } _z_link_events_listener_t _z_link_events_listener_null(void) { return (_z_link_events_listener_t){0}; } void _z_link_events_listener_clear(_z_link_events_listener_t *listener) { _z_session_weak_drop(&listener->_session); _z_sync_group_drop(&listener->_callback_drop_sync_group); *listener = _z_link_events_listener_null(); } static z_result_t _z_link_events_listener_undeclare(_z_link_events_listener_t *listener) { _z_session_rc_t session_rc = _z_session_weak_upgrade(&listener->_session); if (!_Z_RC_IS_NULL(&session_rc)) { _z_session_t *session = _Z_RC_IN_VAL(&session_rc); _z_session_mutex_lock(session); _z_connectivity_link_listener_intmap_remove(&session->_connectivity_link_event_listeners, listener->_id); _z_session_mutex_unlock(session); _z_session_rc_drop(&session_rc); } if (_z_sync_group_check(&listener->_callback_drop_sync_group)) { return _z_sync_group_wait(&listener->_callback_drop_sync_group); } return _Z_RES_OK; } void _z_link_events_listener_drop(_z_link_events_listener_t *listener) { _z_link_events_listener_undeclare(listener); _z_link_events_listener_clear(listener); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_transport_events_listener_t, transport_events_listener, _z_transport_events_listener_check, _z_transport_events_listener_null, _z_transport_events_listener_drop) _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_link_events_listener_t, link_events_listener, _z_link_events_listener_check, _z_link_events_listener_null, _z_link_events_listener_drop) void z_transport_events_listener_options_default(z_transport_events_listener_options_t *options) { options->history = false; } void z_link_events_listener_options_default(z_link_events_listener_options_t *options) { options->history = false; options->transport = NULL; } z_result_t z_declare_transport_events_listener(const z_loaned_session_t *zs, z_owned_transport_events_listener_t *listener, z_moved_closure_transport_event_t *callback, const z_transport_events_listener_options_t *options) { listener->_val = _z_transport_events_listener_null(); if (zs == NULL || _Z_RC_IS_NULL(zs)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_transport_events_listener_options_t opt; z_transport_events_listener_options_default(&opt); if (options != NULL) { opt = *options; } _z_sync_group_t callback_drop_sync_group = _z_sync_group_null(); _Z_RETURN_IF_ERR(_z_sync_group_create(&callback_drop_sync_group)); _z_void_rc_t callback_rc; _Z_CLEAN_RETURN_IF_ERR( _z_connectivity_take_transport_callback(&callback_rc, callback, &_Z_RC_IN_VAL(zs)->_callback_drop_sync_group, &callback_drop_sync_group), _z_sync_group_drop(&callback_drop_sync_group)); _z_connectivity_transport_listener_t *listener_state = (_z_connectivity_transport_listener_t *)z_malloc(sizeof(_z_connectivity_transport_listener_t)); if (listener_state == NULL) { _z_void_rc_drop(&callback_rc); _z_sync_group_drop(&callback_drop_sync_group); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *listener_state = (_z_connectivity_transport_listener_t){._callback = callback_rc}; _z_session_t *session = _Z_RC_IN_VAL(zs); _z_transport_common_t *locked_transport_common = NULL; z_result_t ret = _Z_RES_OK; size_t id = 0; bool listener_registered = false; if (opt.history) { locked_transport_common = _z_transport_get_common(&session->_tp); if (locked_transport_common != NULL) { _z_transport_peer_mutex_lock(locked_transport_common); } } if (opt.history) { _z_void_rc_t callback_snapshot = _z_void_rc_clone(&callback_rc); if (_Z_RC_IS_NULL(&callback_snapshot)) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } _z_connectivity_transport_cb_state_t *callback_state = (_z_connectivity_transport_cb_state_t *)callback_snapshot._val; _z_connectivity_replay_transport_history(session, callback_state); _z_void_rc_drop(&callback_snapshot); } ret = _z_session_mutex_lock_if_open(session); if (ret != _Z_RES_OK) { goto exit; } id = session->_connectivity_next_listener_id++; if (_z_connectivity_transport_listener_intmap_insert(&session->_connectivity_transport_event_listeners, id, listener_state) == NULL) { _z_session_mutex_unlock(session); ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } listener_registered = true; _z_session_mutex_unlock(session); _z_session_weak_t weak = _z_session_rc_clone_as_weak(zs); if (_Z_RC_IS_NULL(&weak)) { _z_session_mutex_lock(session); _z_connectivity_transport_listener_intmap_remove(&session->_connectivity_transport_event_listeners, id); _z_session_mutex_unlock(session); listener_registered = false; listener_state = NULL; ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } listener->_val = (_z_transport_events_listener_t){ ._id = id, ._session = weak, ._callback_drop_sync_group = callback_drop_sync_group, }; callback_drop_sync_group = _z_sync_group_null(); exit: if (!listener_registered && listener_state != NULL) { _z_connectivity_transport_listener_clear(listener_state); z_free(listener_state); } if (locked_transport_common != NULL) { _z_transport_peer_mutex_unlock(locked_transport_common); } _z_sync_group_drop(&callback_drop_sync_group); return ret; } z_result_t z_declare_background_transport_events_listener(const z_loaned_session_t *zs, z_moved_closure_transport_event_t *callback, const z_transport_events_listener_options_t *options) { z_owned_transport_events_listener_t listener; _Z_RETURN_IF_ERR(z_declare_transport_events_listener(zs, &listener, callback, options)); _z_transport_events_listener_clear(&listener._val); return _Z_RES_OK; } z_result_t z_undeclare_transport_events_listener(z_moved_transport_events_listener_t *listener) { z_result_t ret = _z_transport_events_listener_undeclare(&listener->_this._val); _z_transport_events_listener_clear(&listener->_this._val); return ret; } z_result_t z_declare_link_events_listener(const z_loaned_session_t *zs, z_owned_link_events_listener_t *listener, z_moved_closure_link_event_t *callback, z_link_events_listener_options_t *options) { listener->_val = _z_link_events_listener_null(); if (zs == NULL || _Z_RC_IS_NULL(zs)) { _Z_ERROR_RETURN(_Z_ERR_SESSION_CLOSED); } z_link_events_listener_options_t opt; z_link_events_listener_options_default(&opt); if (options != NULL) { opt = *options; } _z_sync_group_t callback_drop_sync_group = _z_sync_group_null(); _Z_RETURN_IF_ERR(_z_sync_group_create(&callback_drop_sync_group)); _z_void_rc_t callback_rc; _Z_CLEAN_RETURN_IF_ERR( _z_connectivity_take_link_callback(&callback_rc, callback, &_Z_RC_IN_VAL(zs)->_callback_drop_sync_group, &callback_drop_sync_group), _z_sync_group_drop(&callback_drop_sync_group)); bool has_transport_filter = false; _z_id_t transport_filter_zid = {0}; bool transport_filter_is_multicast = false; z_result_t ret = _Z_RES_OK; if (opt.transport != NULL) { if (!z_internal_transport_check(&opt.transport->_this)) { ret = _Z_ERR_INVALID; } else { const z_loaned_transport_t *transport = z_transport_loan(&opt.transport->_this); has_transport_filter = true; transport_filter_zid = transport->_zid; transport_filter_is_multicast = transport->_is_multicast; } } z_transport_drop(opt.transport); if (ret != _Z_RES_OK) { _z_void_rc_drop(&callback_rc); _z_sync_group_drop(&callback_drop_sync_group); return ret; } _z_connectivity_link_listener_t *listener_state = (_z_connectivity_link_listener_t *)z_malloc(sizeof(_z_connectivity_link_listener_t)); if (listener_state == NULL) { _z_void_rc_drop(&callback_rc); _z_sync_group_drop(&callback_drop_sync_group); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *listener_state = (_z_connectivity_link_listener_t){ ._callback = callback_rc, ._has_transport_filter = has_transport_filter, ._transport_zid = transport_filter_zid, ._transport_is_multicast = transport_filter_is_multicast, }; _z_session_t *session = _Z_RC_IN_VAL(zs); _z_transport_common_t *locked_transport_common = NULL; size_t id = 0; bool listener_registered = false; if (opt.history) { locked_transport_common = _z_transport_get_common(&session->_tp); if (locked_transport_common != NULL) { _z_transport_peer_mutex_lock(locked_transport_common); } } if (opt.history) { _z_void_rc_t callback_snapshot = _z_void_rc_clone(&callback_rc); if (_Z_RC_IS_NULL(&callback_snapshot)) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } _z_info_transport_t transport_filter = {0}; transport_filter._zid = transport_filter_zid; transport_filter._is_multicast = transport_filter_is_multicast; _z_connectivity_link_cb_state_t *callback_state = (_z_connectivity_link_cb_state_t *)callback_snapshot._val; _z_connectivity_replay_link_history(session, callback_state, has_transport_filter, &transport_filter); _z_void_rc_drop(&callback_snapshot); } ret = _z_session_mutex_lock_if_open(session); if (ret != _Z_RES_OK) { goto exit; } id = session->_connectivity_next_listener_id++; if (_z_connectivity_link_listener_intmap_insert(&session->_connectivity_link_event_listeners, id, listener_state) == NULL) { _z_session_mutex_unlock(session); ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } listener_registered = true; _z_session_mutex_unlock(session); _z_session_weak_t weak = _z_session_rc_clone_as_weak(zs); if (_Z_RC_IS_NULL(&weak)) { _z_session_mutex_lock(session); _z_connectivity_link_listener_intmap_remove(&session->_connectivity_link_event_listeners, id); _z_session_mutex_unlock(session); listener_registered = false; listener_state = NULL; ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto exit; } listener->_val = (_z_link_events_listener_t){ ._id = id, ._session = weak, ._callback_drop_sync_group = callback_drop_sync_group, }; callback_drop_sync_group = _z_sync_group_null(); exit: if (!listener_registered && listener_state != NULL) { _z_connectivity_link_listener_clear(listener_state); z_free(listener_state); } if (locked_transport_common != NULL) { _z_transport_peer_mutex_unlock(locked_transport_common); } _z_sync_group_drop(&callback_drop_sync_group); return ret; } z_result_t z_declare_background_link_events_listener(const z_loaned_session_t *zs, z_moved_closure_link_event_t *callback, z_link_events_listener_options_t *options) { z_owned_link_events_listener_t listener; _Z_RETURN_IF_ERR(z_declare_link_events_listener(zs, &listener, callback, options)); _z_link_events_listener_clear(&listener._val); return _Z_RES_OK; } z_result_t z_undeclare_link_events_listener(z_moved_link_events_listener_t *listener) { z_result_t ret = _z_link_events_listener_undeclare(&listener->_this._val); _z_link_events_listener_clear(&listener->_this._val); return ret; } void _z_connectivity_peer_connected(_z_session_t *session, const _z_connectivity_peer_event_data_t *peer, bool is_multicast, uint16_t mtu, bool is_streamed, bool is_reliable) { if (session == NULL || peer == NULL) { return; } _z_info_transport_event_t transport_event = {0}; transport_event.kind = Z_SAMPLE_KIND_PUT; _z_connectivity_transport_from_event_data(&transport_event.transport, peer, is_multicast); _z_connectivity_dispatch_transport_event(session, &transport_event); _z_info_link_event_t link_event = {0}; link_event.kind = Z_SAMPLE_KIND_PUT; if (_z_connectivity_link_fill(&link_event.link, peer, mtu, is_streamed, is_reliable) == _Z_RES_OK) { _z_connectivity_dispatch_link_event(session, &link_event, is_multicast); } _z_connectivity_link_event_clear(&link_event); } void _z_connectivity_peer_disconnected(_z_session_t *session, const _z_connectivity_peer_event_data_t *peer, bool is_multicast, uint16_t mtu, bool is_streamed, bool is_reliable) { if (session == NULL || peer == NULL) { return; } _z_info_link_event_t link_event = {0}; link_event.kind = Z_SAMPLE_KIND_DELETE; if (_z_connectivity_link_fill(&link_event.link, peer, mtu, is_streamed, is_reliable) == _Z_RES_OK) { _z_connectivity_dispatch_link_event(session, &link_event, is_multicast); } _z_connectivity_link_event_clear(&link_event); _z_info_transport_event_t transport_event = {0}; transport_event.kind = Z_SAMPLE_KIND_DELETE; _z_connectivity_transport_from_event_data(&transport_event.transport, peer, is_multicast); _z_connectivity_dispatch_transport_event(session, &transport_event); } void _z_connectivity_peer_disconnected_from_transport(_z_session_t *session, const _z_transport_common_t *transport, const _z_connectivity_peer_event_data_t *peer, bool is_multicast) { if (peer == NULL) { return; } uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_link_properties_from_transport(transport, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_disconnected(session, peer, is_multicast, mtu, is_streamed, is_reliable); } #endif ================================================ FILE: src/api/encoding.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/utils/string.h" #if Z_FEATURE_ENCODING_VALUES == 1 #define ENCODING_SCHEMA_SEPARATOR ';' #define ENCODING_CONSTANT_MACRO(_cname, _fname, _id) \ const z_owned_encoding_t ZP_ENCODING_##_cname = { \ ._val = { \ .id = _id, \ .schema = {._slice = {.start = NULL, .len = 0, ._delete_context = {.deleter = NULL, .context = NULL}}}, \ }}; \ const z_loaned_encoding_t *z_encoding_##_fname(void) { return &ZP_ENCODING_##_cname._val; } ENCODING_CONSTANT_MACRO(ZENOH_BYTES, zenoh_bytes, 0) ENCODING_CONSTANT_MACRO(ZENOH_STRING, zenoh_string, 1) ENCODING_CONSTANT_MACRO(ZENOH_SERIALIZED, zenoh_serialized, 2) ENCODING_CONSTANT_MACRO(APPLICATION_OCTET_STREAM, application_octet_stream, 3) ENCODING_CONSTANT_MACRO(TEXT_PLAIN, text_plain, 4) ENCODING_CONSTANT_MACRO(APPLICATION_JSON, application_json, 5) ENCODING_CONSTANT_MACRO(TEXT_JSON, text_json, 6) ENCODING_CONSTANT_MACRO(APPLICATION_CDR, application_cdr, 7) ENCODING_CONSTANT_MACRO(APPLICATION_CBOR, application_cbor, 8) ENCODING_CONSTANT_MACRO(APPLICATION_YAML, application_yaml, 9) ENCODING_CONSTANT_MACRO(TEXT_YAML, text_yaml, 10) ENCODING_CONSTANT_MACRO(TEXT_JSON5, text_json5, 11) ENCODING_CONSTANT_MACRO(APPLICATION_PYTHON_SERIALIZED_OBJECT, application_python_serialized_object, 12) ENCODING_CONSTANT_MACRO(APPLICATION_PROTOBUF, application_protobuf, 13) ENCODING_CONSTANT_MACRO(APPLICATION_JAVA_SERIALIZED_OBJECT, application_java_serialized_object, 14) ENCODING_CONSTANT_MACRO(APPLICATION_OPENMETRICS_TEXT, application_openmetrics_text, 15) ENCODING_CONSTANT_MACRO(IMAGE_PNG, image_png, 16) ENCODING_CONSTANT_MACRO(IMAGE_JPEG, image_jpeg, 17) ENCODING_CONSTANT_MACRO(IMAGE_GIF, image_gif, 18) ENCODING_CONSTANT_MACRO(IMAGE_BMP, image_bmp, 19) ENCODING_CONSTANT_MACRO(IMAGE_WEBP, image_webp, 20) ENCODING_CONSTANT_MACRO(APPLICATION_XML, application_xml, 21) ENCODING_CONSTANT_MACRO(APPLICATION_X_WWW_FORM_URLENCODED, application_x_www_form_urlencoded, 22) ENCODING_CONSTANT_MACRO(TEXT_HTML, text_html, 23) ENCODING_CONSTANT_MACRO(TEXT_XML, text_xml, 24) ENCODING_CONSTANT_MACRO(TEXT_CSS, text_css, 25) ENCODING_CONSTANT_MACRO(TEXT_JAVASCRIPT, text_javascript, 26) ENCODING_CONSTANT_MACRO(TEXT_MARKDOWN, text_markdown, 27) ENCODING_CONSTANT_MACRO(TEXT_CSV, text_csv, 28) ENCODING_CONSTANT_MACRO(APPLICATION_SQL, application_sql, 29) ENCODING_CONSTANT_MACRO(APPLICATION_COAP_PAYLOAD, application_coap_payload, 30) ENCODING_CONSTANT_MACRO(APPLICATION_JSON_PATCH_JSON, application_json_patch_json, 31) ENCODING_CONSTANT_MACRO(APPLICATION_JSON_SEQ, application_json_seq, 32) ENCODING_CONSTANT_MACRO(APPLICATION_JSONPATH, application_jsonpath, 33) ENCODING_CONSTANT_MACRO(APPLICATION_JWT, application_jwt, 34) ENCODING_CONSTANT_MACRO(APPLICATION_MP4, application_mp4, 35) ENCODING_CONSTANT_MACRO(APPLICATION_SOAP_XML, application_soap_xml, 36) ENCODING_CONSTANT_MACRO(APPLICATION_YANG, application_yang, 37) ENCODING_CONSTANT_MACRO(AUDIO_AAC, audio_aac, 38) ENCODING_CONSTANT_MACRO(AUDIO_FLAC, audio_flac, 39) ENCODING_CONSTANT_MACRO(AUDIO_MP4, audio_mp4, 40) ENCODING_CONSTANT_MACRO(AUDIO_OGG, audio_ogg, 41) ENCODING_CONSTANT_MACRO(AUDIO_VORBIS, audio_vorbis, 42) ENCODING_CONSTANT_MACRO(VIDEO_H261, video_h261, 43) ENCODING_CONSTANT_MACRO(VIDEO_H263, video_h263, 44) ENCODING_CONSTANT_MACRO(VIDEO_H264, video_h264, 45) ENCODING_CONSTANT_MACRO(VIDEO_H265, video_h265, 46) ENCODING_CONSTANT_MACRO(VIDEO_H266, video_h266, 47) ENCODING_CONSTANT_MACRO(VIDEO_MP4, video_mp4, 48) ENCODING_CONSTANT_MACRO(VIDEO_OGG, video_ogg, 49) ENCODING_CONSTANT_MACRO(VIDEO_RAW, video_raw, 50) ENCODING_CONSTANT_MACRO(VIDEO_VP8, video_vp8, 51) ENCODING_CONSTANT_MACRO(VIDEO_VP9, video_vp9, 52) const char *ENCODING_VALUES_ID_TO_STR[] = { "zenoh/bytes", "zenoh/string", "zenoh/serialized", "application/octet-stream", "text/plain", "application/json", "text/json", "application/cdr", "application/cbor", "application/yaml", "text/yaml", "text/json5", "application/python-serialized-object", "application/protobuf", "application/java-serialized-object", "application/openmetrics-text", "image/png", "image/jpeg", "image/gif", "image/bmp", "image/webp", "application/xml", "application/x-www-form-urlencoded", "text/html", "text/xml", "text/css", "text/javascript", "text/markdown", "text/csv", "application/sql", "application/coap-payload", "application/json-patch+json", "application/json-seq", "application/jsonpath", "application/jwt", "application/mp4", "application/soap+xml", "application/yang", "audio/aac", "audio/flac", "audio/mp4", "audio/ogg", "audio/vorbis", "video/h261", "video/h263", "video/h264", "video/h265", "video/h266", "video/mp4", "video/ogg", "video/raw", "video/vp8", "video/vp9", }; static uint16_t _z_encoding_values_str_to_int(const char *schema, size_t len) { for (size_t i = 0; i < _ZP_ARRAY_SIZE(ENCODING_VALUES_ID_TO_STR); i++) { if (strncmp(schema, ENCODING_VALUES_ID_TO_STR[i], len) == 0) { return (uint16_t)i; } } return UINT16_MAX; } static z_result_t _z_encoding_convert_from_substr(z_owned_encoding_t *encoding, const char *s, size_t len) { size_t pos = 0; for (; pos < len; ++pos) { if (s[pos] == ENCODING_SCHEMA_SEPARATOR) break; } // Check id_end value + corner cases if (pos != 0) { uint16_t id = _z_encoding_values_str_to_int(s, pos); // Check id if (id != UINT16_MAX) { const char *ptr = NULL; size_t remaining = 0; if (pos + 1 < len) { ptr = s + pos + 1; remaining = len - pos - 1; } return _z_encoding_make(&encoding->_val, id, ptr, remaining); } } // By default store the string as schema return _z_encoding_make(&encoding->_val, _Z_ENCODING_ID_DEFAULT, s, len); } static z_result_t _z_encoding_convert_into_string(const z_loaned_encoding_t *encoding, z_owned_string_t *s) { const char *prefix = NULL; size_t prefix_len = 0; // Convert id if (encoding->id < _ZP_ARRAY_SIZE(ENCODING_VALUES_ID_TO_STR)) { prefix = ENCODING_VALUES_ID_TO_STR[encoding->id]; prefix_len = strlen(prefix); } bool has_schema = _z_string_len(&encoding->schema) > 0; // Size include null terminator size_t total_len = prefix_len + _z_string_len(&encoding->schema) + 1; // Check for schema separator if (has_schema) { total_len += 1; } // Allocate string char *value = (char *)z_malloc(sizeof(char) * total_len); if (value == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } memset(value, 0, total_len); // Copy prefix (void)strncpy(value, prefix, prefix_len); // Copy schema and separator if (has_schema) { _z_str_append(value, ENCODING_SCHEMA_SEPARATOR); (void)strncat(value, _z_string_data(&encoding->schema), _z_string_len(&encoding->schema)); } // Fill container s->_val = _z_string_from_str_custom_deleter(value, _z_delete_context_default()); return _Z_RES_OK; } #else static z_result_t _z_encoding_convert_from_substr(z_owned_encoding_t *encoding, const char *s, size_t len) { return _z_encoding_make(&encoding->_val, _Z_ENCODING_ID_DEFAULT, s, len); } static z_result_t _z_encoding_convert_into_string(const z_loaned_encoding_t *encoding, z_owned_string_t *s) { _z_string_copy(&s->_val, &encoding->schema); return _Z_RES_OK; } #endif z_result_t z_encoding_from_str(z_owned_encoding_t *encoding, const char *s) { // Init owned encoding z_internal_encoding_null(encoding); // Convert string to encoding if (s != NULL) { return _z_encoding_convert_from_substr(encoding, s, strlen(s)); } return _Z_RES_OK; } z_result_t z_encoding_from_substr(z_owned_encoding_t *encoding, const char *s, size_t len) { // Init owned encoding z_internal_encoding_null(encoding); // Convert string to encoding if (s != NULL) { return _z_encoding_convert_from_substr(encoding, s, len); } return _Z_RES_OK; } z_result_t z_encoding_set_schema_from_str(z_loaned_encoding_t *encoding, const char *schema) { return z_encoding_set_schema_from_substr(encoding, schema, strlen(schema)); } z_result_t z_encoding_set_schema_from_substr(z_loaned_encoding_t *encoding, const char *schema, size_t len) { _z_string_clear(&encoding->schema); if (schema == NULL && len > 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } encoding->schema = _z_string_copy_from_substr(schema, len); if (_z_string_len(&encoding->schema) != len) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } z_result_t z_encoding_to_string(const z_loaned_encoding_t *encoding, z_owned_string_t *s) { // Init owned string z_internal_string_null(s); // Convert encoding to string _z_encoding_convert_into_string(encoding, s); return _Z_RES_OK; } #if Z_FEATURE_ENCODING_VALUES == 1 const z_loaned_encoding_t *z_encoding_loan_default(void) { return z_encoding_zenoh_bytes(); } #endif ================================================ FILE: src/api/liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/net/liveliness.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_LIVELINESS == 1 /**************** Liveliness Token ****************/ bool _z_liveliness_token_check(const _z_liveliness_token_t *token) { return !_Z_RC_IS_NULL(&token->_zn); } _z_liveliness_token_t _z_liveliness_token_null(void) { _z_liveliness_token_t s = {0}; return s; } z_result_t _z_liveliness_token_clear(_z_liveliness_token_t *token) { z_result_t ret = _Z_RES_OK; if (_Z_RC_IS_NULL(&token->_zn)) { return ret; } _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&token->_zn); if (!_Z_RC_IS_NULL(&sess_rc)) { ret = _z_undeclare_liveliness_token(token); _z_session_rc_drop(&sess_rc); } _z_session_weak_drop(&token->_zn); *token = _z_liveliness_token_null(); return ret; } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_NO_MOVE_IMPL(_z_liveliness_token_t, liveliness_token, _z_liveliness_token_check, _z_liveliness_token_null, _z_liveliness_token_clear) z_result_t z_liveliness_token_options_default(z_liveliness_token_options_t *options) { options->__dummy = 0; return _Z_RES_OK; } z_result_t z_liveliness_declare_token(const z_loaned_session_t *zs, z_owned_liveliness_token_t *token, const z_loaned_keyexpr_t *keyexpr, const z_liveliness_token_options_t *options) { (void)options; return _z_declare_liveliness_token(zs, &token->_val, keyexpr); } z_result_t z_liveliness_undeclare_token(z_moved_liveliness_token_t *token) { return _z_liveliness_token_clear(&token->_this._val); } /**************** Liveliness Subscriber ****************/ #if Z_FEATURE_SUBSCRIPTION == 1 z_result_t z_liveliness_subscriber_options_default(z_liveliness_subscriber_options_t *options) { options->history = false; return _Z_RES_OK; } z_result_t z_liveliness_declare_subscriber(const z_loaned_session_t *zs, z_owned_subscriber_t *sub, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options) { _z_closure_sample_t closure = callback->_this._val; z_internal_closure_sample_null(&callback->_this); z_liveliness_subscriber_options_t opt; if (options == NULL) { z_liveliness_subscriber_options_default(&opt); } else { opt = *options; } if (sub != NULL) { sub->_val = _z_subscriber_null(); return _z_declare_liveliness_subscriber(&sub->_val, zs, keyexpr, closure.call, closure.drop, opt.history, closure.context); } else { uint32_t _sub_id; return _z_register_liveliness_subscriber(&_sub_id, zs, keyexpr, closure.call, closure.drop, opt.history, closure.context, NULL); } } z_result_t z_liveliness_declare_background_subscriber(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_sample_t *callback, z_liveliness_subscriber_options_t *options) { return z_liveliness_declare_subscriber(zs, NULL, keyexpr, callback, options); } #endif // Z_FEATURE_SUBSCRIPTION == 1 /**************** Liveliness Query ****************/ #if Z_FEATURE_QUERY == 1 z_result_t z_liveliness_get_options_default(z_liveliness_get_options_t *options) { options->timeout_ms = 0; #ifdef Z_FEATURE_UNSTABLE_API options->cancellation_token = NULL; #endif return _Z_RES_OK; } z_result_t z_liveliness_get(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, z_moved_closure_reply_t *callback, z_liveliness_get_options_t *options) { z_result_t ret = _Z_RES_OK; void *ctx = callback->_this._val.context; callback->_this._val.context = NULL; z_liveliness_get_options_t opt; if (options == NULL) { z_liveliness_get_options_default(&opt); } else { opt = *options; } if (opt.timeout_ms == 0) { opt.timeout_ms = Z_GET_TIMEOUT_DEFAULT; } _z_cancellation_token_rc_t *cancellation_token = NULL; #ifdef Z_FEATURE_UNSTABLE_API cancellation_token = opt.cancellation_token == NULL ? NULL : &opt.cancellation_token->_this._rc; #else #endif ret = _z_liveliness_query(zs, keyexpr, callback->_this._val.call, callback->_this._val.drop, ctx, opt.timeout_ms, cancellation_token); #ifdef Z_FEATURE_UNSTABLE_API z_cancellation_token_drop(opt.cancellation_token); #endif z_internal_closure_reply_null( &callback->_this); // call and drop passed to _z_liveliness_query, so we nullify the closure here return ret; } #endif // Z_FEATURE_QUERY == 1 #endif // Z_FEATURE_LIVELINESS == 1 ================================================ FILE: src/api/serialization.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/api/serialization.h" #include #include "zenoh-pico/protocol/codec/core.h" bool _ze_serializer_check(const _ze_serializer_t *serializer) { return _z_bytes_writer_check(&serializer->_writer); } _ze_serializer_t _ze_serializer_empty(void) { _ze_serializer_t s; s._writer = _z_bytes_writer_empty(); return s; } void _ze_serializer_clear(_ze_serializer_t *serializer) { _z_bytes_writer_clear(&serializer->_writer); } z_result_t _ze_serializer_move(_ze_serializer_t *dst, _ze_serializer_t *src) { return _z_bytes_writer_move(&dst->_writer, &src->_writer); } _Z_OWNED_FUNCTIONS_VALUE_NO_COPY_IMPL_PREFIX(ze, _ze_serializer_t, serializer, _ze_serializer_check, _ze_serializer_empty, _ze_serializer_move, _ze_serializer_clear) z_result_t ze_serializer_empty(ze_owned_serializer_t *serializer) { serializer->_val._writer = _z_bytes_writer_empty(); return _Z_RES_OK; } void ze_serializer_finish(ze_moved_serializer_t *serializer, z_owned_bytes_t *bytes) { bytes->_val = _z_bytes_writer_finish(&serializer->_this._val._writer); } ze_deserializer_t ze_deserializer_from_bytes(const z_loaned_bytes_t *bytes) { ze_deserializer_t d; d._reader = z_bytes_get_reader(bytes); return d; } z_result_t __read_single_byte(uint8_t *b, void *context) { z_bytes_reader_t *reader = (z_bytes_reader_t *)context; if (_z_bytes_reader_read(reader, b, 1) != 1) { _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); } return _Z_RES_OK; } z_result_t __read_zint(z_bytes_reader_t *reader, _z_zint_t *zint) { return _z_zsize_decode_with_reader(zint, __read_single_byte, reader); } z_result_t ze_serializer_serialize_sequence_length(ze_loaned_serializer_t *serializer, size_t len) { uint8_t buf[16]; size_t bytes_used = _z_zsize_encode_buf(buf, len); return _z_bytes_writer_write_all(&serializer->_writer, buf, bytes_used); } z_result_t ze_deserializer_deserialize_sequence_length(ze_deserializer_t *deserializer, size_t *len) { return __read_zint(&deserializer->_reader, len); } z_result_t ze_serializer_serialize_buf(ze_loaned_serializer_t *serializer, const uint8_t *val, size_t len) { _Z_RETURN_IF_ERR(ze_serializer_serialize_sequence_length(serializer, len)); _Z_RETURN_IF_ERR(_z_bytes_writer_write_all(&serializer->_writer, val, len)); return Z_OK; } z_result_t ze_serializer_serialize_slice(ze_loaned_serializer_t *serializer, const z_loaned_slice_t *val) { return ze_serializer_serialize_buf(serializer, z_slice_data(val), z_slice_len(val)); } z_result_t ze_deserializer_deserialize_slice(ze_deserializer_t *deserializer, z_owned_slice_t *val) { size_t len = 0; _Z_RETURN_IF_ERR(ze_deserializer_deserialize_sequence_length(deserializer, &len)); _Z_RETURN_IF_ERR(_z_slice_init(&val->_val, len)); if (z_bytes_reader_read(&deserializer->_reader, (uint8_t *)val->_val.start, len) != len) { _z_slice_clear(&val->_val); _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); }; return Z_OK; } z_result_t ze_serializer_serialize_substr(ze_loaned_serializer_t *serializer, const char *start, size_t len) { // TODO: perform a UTF-8 correctness check. return ze_serializer_serialize_buf(serializer, (const uint8_t *)start, len); } z_result_t ze_serializer_serialize_str(ze_loaned_serializer_t *serializer, const char *val) { return ze_serializer_serialize_substr(serializer, val, strlen(val)); } z_result_t ze_serializer_serialize_string(ze_loaned_serializer_t *serializer, const z_loaned_string_t *val) { return ze_serializer_serialize_buf(serializer, (const uint8_t *)z_string_data(val), z_string_len(val)); } z_result_t ze_deserializer_deserialize_string(ze_deserializer_t *deserializer, z_owned_string_t *val) { z_owned_slice_t s; _Z_RETURN_IF_ERR(ze_deserializer_deserialize_slice(deserializer, &s)); val->_val._slice = s._val; return _Z_RES_OK; } #define _Z_BUILD_BYTES_FROM_SERIALIZER(expr) \ z_bytes_empty(bytes); \ _ze_serializer_t serializer = _ze_serializer_empty(); \ _Z_CLEAN_RETURN_IF_ERR(expr, _ze_serializer_clear(&serializer)); \ bytes->_val = _z_bytes_writer_finish(&serializer._writer); z_result_t ze_serialize_buf(z_owned_bytes_t *bytes, const uint8_t *data, size_t len) { _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_buf(&serializer, data, len)); return _Z_RES_OK; } z_result_t ze_serialize_slice(z_owned_bytes_t *bytes, const z_loaned_slice_t *data) { _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_slice(&serializer, data)); return _Z_RES_OK; } z_result_t ze_deserialize_slice(const z_loaned_bytes_t *bytes, z_owned_slice_t *data) { ze_deserializer_t deserializer = ze_deserializer_from_bytes(bytes); return ze_deserializer_deserialize_slice(&deserializer, data); } z_result_t ze_serialize_substr(z_owned_bytes_t *bytes, const char *start, size_t len) { _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_substr(&serializer, start, len)); return _Z_RES_OK; } z_result_t ze_serialize_str(z_owned_bytes_t *bytes, const char *data) { _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_str(&serializer, data)); return _Z_RES_OK; } z_result_t ze_serialize_string(z_owned_bytes_t *bytes, const z_loaned_string_t *data) { _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_string(&serializer, data)); return _Z_RES_OK; } z_result_t ze_deserialize_string(const z_loaned_bytes_t *bytes, z_owned_string_t *data) { ze_deserializer_t deserializer = ze_deserializer_from_bytes(bytes); return ze_deserializer_deserialize_string(&deserializer, data); } #define _Z_IMPLEMENT_ZBYTES_ARITHMETIC(suffix, type) \ z_result_t ze_serialize_##suffix(z_owned_bytes_t *bytes, type data) { \ _Z_BUILD_BYTES_FROM_SERIALIZER(ze_serializer_serialize_##suffix(&serializer, data)) \ return _Z_RES_OK; \ } \ z_result_t ze_deserialize_##suffix(const z_loaned_bytes_t *bytes, type *data) { \ ze_deserializer_t deserializer = ze_deserializer_from_bytes(bytes); \ z_result_t err = ze_deserializer_deserialize_##suffix(&deserializer, data); \ if (err == Z_OK && !ze_deserializer_is_done(&deserializer)) { \ err = Z_EDESERIALIZE; \ } \ return err; \ } _Z_IMPLEMENT_ZBYTES_ARITHMETIC(uint8, uint8_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(uint16, uint16_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(uint32, uint32_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(uint64, uint64_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(int8, int8_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(int16, int16_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(int32, int32_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(int64, int64_t) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(float, float) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(double, double) _Z_IMPLEMENT_ZBYTES_ARITHMETIC(bool, bool) bool ze_deserializer_is_done(const ze_deserializer_t *deserializer) { return _z_bytes_reader_remaining(&deserializer->_reader) == 0; } ================================================ FILE: src/collections/advanced_cache.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/advanced_cache.h" #include #include #include #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/time_range.h" #define _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED -1 #define _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_MAX_UNBOUNDED SIZE_MAX #if Z_FEATURE_ADVANCED_PUBLICATION == 1 typedef struct { int64_t start; int64_t end; } _ze_advanced_cache_range_t; typedef struct { _ze_advanced_cache_range_t range; size_t max; _z_time_range_t time; } _ze_advanced_cache_query_parameters_t; static bool _ze_advanced_cache_query_match_key(const char *key_start, size_t key_len, const char *expected_key, size_t expected_key_len) { return (key_len == expected_key_len && strncmp(key_start, expected_key, key_len) == 0); } static void _ze_advanced_cache_query_parse_max(const _z_str_se_t *str, size_t *max) { *max = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_MAX_UNBOUNDED; if (_z_ptr_char_diff(str->end, str->start) > 0) { uint32_t value; if (_z_str_se_atoui(str, &value)) { *max = value; } } } static void _ze_advanced_cache_query_parse_range(const _z_str_se_t *str, _ze_advanced_cache_range_t *range) { range->start = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED; range->end = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED; if (_z_ptr_char_diff(str->end, str->start) > 0) { _z_splitstr_t ss = {.s = *str, .delimiter = ".."}; _z_str_se_t token = _z_splitstr_next(&ss); uint32_t value; if (_z_str_se_atoui(&token, &value)) { range->start = value; } token = _z_splitstr_next(&ss); if (_z_str_se_atoui(&token, &value)) { range->end = value; } } } static void _ze_advanced_cache_query_parse_time(const _z_str_se_t *str, _z_time_range_t *time) { _z_time_range_t range; if (_z_time_range_from_str(str->start, _z_ptr_char_diff(str->end, str->start), &range)) { *time = range; } else { time->start.bound = _Z_TIME_BOUND_UNBOUNDED; time->end.bound = _Z_TIME_BOUND_UNBOUNDED; } } static void _ze_advanced_cache_query_parse_parameters(_ze_advanced_cache_query_parameters_t *params, const z_loaned_string_t *raw_params) { params->range.start = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED; params->range.end = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED; params->max = _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_MAX_UNBOUNDED; params->time.start.bound = _Z_TIME_BOUND_UNBOUNDED; params->time.end.bound = _Z_TIME_BOUND_UNBOUNDED; _z_str_se_t str; str.start = z_string_data(raw_params); str.end = str.start + z_string_len(raw_params); while (str.start != NULL) { _z_query_param_t param = _z_query_params_next(&str); if (param.key.start != NULL) { size_t key_len = _z_ptr_char_diff(param.key.end, param.key.start); if (_ze_advanced_cache_query_match_key(param.key.start, key_len, _Z_QUERY_PARAMS_KEY_RANGE, _Z_QUERY_PARAMS_KEY_RANGE_LEN)) { _ze_advanced_cache_query_parse_range(¶m.value, ¶ms->range); } else if (_ze_advanced_cache_query_match_key(param.key.start, key_len, _Z_QUERY_PARAMS_KEY_MAX, _Z_QUERY_PARAMS_KEY_MAX_LEN)) { _ze_advanced_cache_query_parse_max(¶m.value, ¶ms->max); } else if (_ze_advanced_cache_query_match_key(param.key.start, key_len, _Z_QUERY_PARAMS_KEY_TIME, _Z_QUERY_PARAMS_KEY_TIME_LEN)) { _ze_advanced_cache_query_parse_time(¶m.value, ¶ms->time); } } } } static bool _ze_advanced_cache_range_contains(const _ze_advanced_cache_range_t *range, uint32_t sn) { return ((range->start == _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED || range->start <= sn) && (range->end == _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED || sn <= range->end)); } static void _ze_advanced_cache_query_handler(z_loaned_query_t *query, void *ctx) { _ze_advanced_cache_t *cache = (_ze_advanced_cache_t *)ctx; z_view_string_t param_str; z_query_parameters(query, ¶m_str); _ze_advanced_cache_query_parameters_t params; _ze_advanced_cache_query_parse_parameters(¶ms, z_view_string_loan(¶m_str)); _z_time_since_epoch now; z_result_t res = _z_get_time_since_epoch(&now); if (res != _Z_RES_OK) { _Z_ERROR("Dropped advanced cache query - failed to determine current system time: %i", res); return; } _z_ntp64_t now_ntp64 = _z_timestamp_ntp64_from_time(now.secs, now.nanos); #if Z_FEATURE_MULTI_THREAD == 1 res = _z_mutex_lock(&cache->_outbox_mutex); if (res != _Z_RES_OK) { _Z_ERROR("Dropped advanced cache query - failed to lock outbox mutex: %i", res); return; } res = _z_mutex_lock(&cache->_mutex); if (res != _Z_RES_OK) { _Z_ERROR("Dropped advanced cache query - failed to lock mutex: %i", res); _z_mutex_unlock(&cache->_outbox_mutex); return; } #endif size_t cap = _z_sample_ring_capacity(&cache->_cache); size_t max = (params.max != _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_MAX_UNBOUNDED) ? params.max : cap; if (max > cap) max = cap; if (max > cache->_outbox_cap) max = cache->_outbox_cap; const bool range_filter = (params.range.start != _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED) || (params.range.end != _ZE_ADVANCED_CACHE_QUERY_PARAMETERS_RANGE_UNBOUNDED); const bool time_filter = (params.time.start.bound != _Z_TIME_BOUND_UNBOUNDED) || (params.time.end.bound != _Z_TIME_BOUND_UNBOUNDED); _z_sample_ring_reverse_iterator_t iter = _z_sample_ring_reverse_iterator_make(&cache->_cache); size_t to_send = 0; while (max > 0 && _z_sample_ring_reverse_iterator_next(&iter)) { _z_sample_t *sample = _z_sample_ring_reverse_iterator_value(&iter); if (range_filter && (!_z_source_info_check(&sample->source_info) || !_ze_advanced_cache_range_contains(¶ms.range, sample->source_info._source_sn))) { continue; } if (time_filter && (!_z_timestamp_check(&sample->timestamp) || !_z_time_range_contains_at_time(¶ms.time, sample->timestamp.time, now_ntp64))) { continue; } res = _z_sample_copy(&cache->_outbox[to_send], sample); if (res != _Z_RES_OK) { _Z_ERROR("Sample dropped from advanced cache query reply - failed to copy sample: %i", res); } else { to_send++; max--; } } #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&cache->_mutex); #endif z_query_reply_options_t opt; z_query_reply_options_default(&opt); opt.congestion_control = cache->_congestion_control; opt.priority = cache->_priority; opt.is_express = cache->_is_express; // Send samples in order while (to_send > 0) { to_send--; _z_sample_t *sample = &cache->_outbox[to_send]; res = _z_query_reply_sample(query, sample, &opt); _z_sample_clear(sample); if (res != _Z_RES_OK) { _Z_ERROR("Sample dropped from advanced cache query reply - failed to send sample: %i", res); } } #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&cache->_outbox_mutex); #endif } static z_result_t _ze_advanced_cache_init(_ze_advanced_cache_t *cache, const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const z_loaned_keyexpr_t *suffix, const ze_advanced_publisher_cache_options_t options) { if (options.max_samples == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _Z_RETURN_IF_ERR(_z_sample_ring_init(&cache->_cache, options.max_samples)); cache->_outbox_cap = options.max_samples; cache->_outbox = (_z_sample_t *)z_malloc(sizeof(_z_sample_t) * cache->_outbox_cap); if (cache->_outbox == NULL) { _z_sample_ring_clear(&cache->_cache); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } memset(cache->_outbox, 0, sizeof(_z_sample_t) * cache->_outbox_cap); cache->_congestion_control = options.congestion_control; cache->_priority = options.priority; cache->_is_express = options.is_express; z_owned_keyexpr_t ke; z_internal_keyexpr_null(&ke); if (suffix != NULL) { _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_join(&ke, keyexpr, suffix), _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL); } else { _Z_CLEAN_RETURN_IF_ERR(z_keyexpr_clone(&ke, keyexpr), _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL); } #if Z_FEATURE_MULTI_THREAD == 1 _Z_CLEAN_RETURN_IF_ERR(_z_mutex_init(&cache->_mutex), z_keyexpr_drop(z_keyexpr_move(&ke)); _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL); _Z_CLEAN_RETURN_IF_ERR(_z_mutex_init(&cache->_outbox_mutex), z_keyexpr_drop(z_keyexpr_move(&ke)); _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL; _z_mutex_drop(&cache->_mutex)); #endif z_result_t res = _Z_RES_OK; z_internal_liveliness_token_null(&cache->_liveliness); if (options._liveliness) { res = z_liveliness_declare_token(zs, &cache->_liveliness, z_keyexpr_loan(&ke), NULL); if (res != _Z_RES_OK) { z_keyexpr_drop(z_keyexpr_move(&ke)); _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&cache->_mutex); _z_mutex_drop(&cache->_outbox_mutex); #endif _Z_ERROR_RETURN(res); } } z_internal_queryable_null(&cache->_queryable); z_owned_closure_query_t callback; res = z_closure_query(&callback, _ze_advanced_cache_query_handler, NULL, cache); if (res != _Z_RES_OK) { z_keyexpr_drop(z_keyexpr_move(&ke)); z_liveliness_token_drop(z_liveliness_token_move(&cache->_liveliness)); _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&cache->_mutex); _z_mutex_drop(&cache->_outbox_mutex); #endif _Z_ERROR_RETURN(res); } res = z_declare_queryable(zs, &cache->_queryable, z_keyexpr_loan(&ke), z_closure_query_move(&callback), NULL); if (res != _Z_RES_OK) { z_keyexpr_drop(z_keyexpr_move(&ke)); z_liveliness_token_drop(z_liveliness_token_move(&cache->_liveliness)); z_closure_query_drop(z_closure_query_move(&callback)); _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); cache->_outbox = NULL; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&cache->_mutex); _z_mutex_drop(&cache->_outbox_mutex); #endif _Z_ERROR_RETURN(res); } z_keyexpr_drop(z_keyexpr_move(&ke)); return res; } _ze_advanced_cache_t *_ze_advanced_cache_new(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *keyexpr, const z_loaned_keyexpr_t *suffix, const ze_advanced_publisher_cache_options_t options) { _ze_advanced_cache_t *cache = (_ze_advanced_cache_t *)z_malloc(sizeof(_ze_advanced_cache_t)); if (cache == NULL) { _Z_ERROR("Failed to create advanced cache: out of memory"); return NULL; } z_result_t ret = _ze_advanced_cache_init(cache, zs, keyexpr, suffix, options); if (ret != _Z_RES_OK) { z_free(cache); return NULL; } return cache; } z_result_t _ze_advanced_cache_add(_ze_advanced_cache_t *cache, _z_sample_t *sample) { if (cache == NULL || sample == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _z_sample_t *s = (_z_sample_t *)z_malloc(sizeof(_z_sample_t)); if (s == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_z_sample_move(s, sample), z_free((void *)s)); #if Z_FEATURE_MULTI_THREAD == 1 _Z_CLEAN_RETURN_IF_ERR(_z_mutex_lock(&cache->_mutex), z_free((void *)s)); #endif _z_sample_ring_push_force_drop(&cache->_cache, s); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&cache->_mutex); #endif return _Z_RES_OK; } void _ze_advanced_cache_free(_ze_advanced_cache_t **pcache) { _ze_advanced_cache_t *cache = (_ze_advanced_cache_t *)*pcache; if (cache != NULL) { z_queryable_drop(z_queryable_move(&cache->_queryable)); z_liveliness_token_drop(z_liveliness_token_move(&cache->_liveliness)); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_lock(&cache->_outbox_mutex); _z_mutex_lock(&cache->_mutex); #endif _z_sample_ring_clear(&cache->_cache); z_free(cache->_outbox); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&cache->_mutex); _z_mutex_drop(&cache->_mutex); _z_mutex_unlock(&cache->_outbox_mutex); _z_mutex_drop(&cache->_outbox_mutex); #endif z_free(cache); *pcache = NULL; } } #endif // Z_FEATURE_ADVANCED_PUBLICATION == 1 ================================================ FILE: src/collections/arc_slice.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/arc_slice.h" #include #include #include #include _z_arc_slice_t _z_arc_slice_wrap(_z_slice_t* s, size_t offset, size_t len) { assert(offset + len <= s->len); _z_arc_slice_t arc_s; arc_s.slice = _z_slice_simple_rc_new_from_val(s); if (_z_slice_simple_rc_is_null(&arc_s.slice)) { return _z_arc_slice_empty(); } arc_s.len = len; arc_s.start = offset; return arc_s; } _z_arc_slice_t _z_arc_slice_get_subslice(const _z_arc_slice_t* s, size_t offset, size_t len) { assert(offset + len <= s->len); assert(!_z_slice_simple_rc_is_null(&s->slice) || (len == 0 && offset == 0)); _z_arc_slice_t out; out.slice = _z_slice_simple_rc_clone(&s->slice); out.len = len; out.start = s->start + offset; return out; } z_result_t _z_arc_slice_copy(_z_arc_slice_t* dst, const _z_arc_slice_t* src) { _z_slice_simple_rc_copy(&dst->slice, &src->slice); dst->len = src->len; dst->start = src->start; return _Z_RES_OK; } z_result_t _z_arc_slice_move(_z_arc_slice_t* dst, _z_arc_slice_t* src) { dst->slice = src->slice; dst->len = src->len; dst->start = src->start; src->len = 0; src->start = 0; src->slice = _z_slice_simple_rc_null(); return _Z_RES_OK; } ================================================ FILE: src/collections/atomic.c ================================================ #include "zenoh-pico/collections/atomic.h" #include #include #include "zenoh-pico/config.h" #if Z_FEATURE_MULTI_THREAD == 1 #if ZENOH_C_STANDARD != 99 #ifndef __cplusplus #include typedef _Atomic(size_t) _z_atomic_t; _Static_assert(sizeof(size_t) == sizeof(_z_atomic_t), "_Atomic(size_t) must have same size as size_t"); static const memory_order _z_memory_order_map[] = {memory_order_relaxed, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst}; void _z_atomic_size_init(_z_atomic_size_t *var, size_t value) { atomic_init((_z_atomic_t *)&var->_value, value); } size_t _z_atomic_size_load(_z_atomic_size_t *var, _z_memory_order_t order) { return atomic_load_explicit((_z_atomic_t *)&var->_value, _z_memory_order_map[order]); } void _z_atomic_size_store(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { atomic_store_explicit((_z_atomic_t *)&var->_value, val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_add(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return atomic_fetch_add_explicit((_z_atomic_t *)&var->_value, val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_sub(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return atomic_fetch_sub_explicit((_z_atomic_t *)&var->_value, val, _z_memory_order_map[order]); } bool _z_atomic_size_compare_exchange_strong(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { // to silence C4100 warning on MSVC (void)success; (void)failure; return atomic_compare_exchange_strong_explicit((_z_atomic_t *)&var->_value, expected, desired, _z_memory_order_map[success], _z_memory_order_map[failure]); } bool _z_atomic_size_compare_exchange_weak(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { // to silence C4100 warning on MSVC (void)success; (void)failure; return atomic_compare_exchange_weak_explicit((_z_atomic_t *)&var->_value, expected, desired, _z_memory_order_map[success], _z_memory_order_map[failure]); } void _z_atomic_thread_fence(_z_memory_order_t order) { atomic_thread_fence(_z_memory_order_map[order]); } #else #include static_assert(sizeof(size_t) == sizeof(std::atomic), "std::atomic must have the same size as size_t"); static const std::memory_order _z_memory_order_map[] = {std::memory_order_relaxed, std::memory_order_acquire, std::memory_order_release, std::memory_order_acq_rel, std::memory_order_seq_cst}; typedef std::atomic _z_atomic_t; void _z_atomic_size_init(_z_atomic_size_t *var, size_t value) { reinterpret_cast<_z_atomic_t *>(&var->_value)->store(value, _z_memory_order_map[_z_memory_order_relaxed]); } size_t _z_atomic_size_load(_z_atomic_size_t *var, _z_memory_order_t order) { return reinterpret_cast<_z_atomic_t *>(&var->_value)->load(_z_memory_order_map[order]); } void _z_atomic_size_store(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { reinterpret_cast<_z_atomic_t *>(&var->_value)->store(val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_add(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return reinterpret_cast<_z_atomic_t *>(&var->_value)->fetch_add(val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_sub(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return reinterpret_cast<_z_atomic_t *>(&var->_value)->fetch_sub(val, _z_memory_order_map[order]); } bool _z_atomic_size_compare_exchange_strong(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { return reinterpret_cast<_z_atomic_t *>(&var->_value) ->compare_exchange_strong(*expected, desired, _z_memory_order_map[success], _z_memory_order_map[failure]); } bool _z_atomic_size_compare_exchange_weak(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { return reinterpret_cast<_z_atomic_t *>(&var->_value) ->compare_exchange_weak(*expected, desired, _z_memory_order_map[success], _z_memory_order_map[failure]); } void _z_atomic_thread_fence(_z_memory_order_t order) { std::atomic_thread_fence(_z_memory_order_map[order]); } #endif #else #ifdef ZENOH_COMPILER_GCC static const int _z_memory_order_map[] = {__ATOMIC_RELAXED, __ATOMIC_ACQUIRE, __ATOMIC_RELEASE, __ATOMIC_ACQ_REL, __ATOMIC_SEQ_CST}; void _z_atomic_size_init(_z_atomic_size_t *var, size_t value) { __atomic_store_n(&var->_value, value, _z_memory_order_map[_z_memory_order_relaxed]); } size_t _z_atomic_size_load(_z_atomic_size_t *var, _z_memory_order_t order) { return __atomic_load_n(&var->_value, _z_memory_order_map[order]); } void _z_atomic_size_store(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { __atomic_store_n(&var->_value, val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_add(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return __atomic_fetch_add(&var->_value, val, _z_memory_order_map[order]); } size_t _z_atomic_size_fetch_sub(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { return __atomic_fetch_sub(&var->_value, val, _z_memory_order_map[order]); } bool _z_atomic_size_compare_exchange_strong(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { return __atomic_compare_exchange_n(&var->_value, expected, desired, false, _z_memory_order_map[success], _z_memory_order_map[failure]); } bool _z_atomic_size_compare_exchange_weak(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { return __atomic_compare_exchange_n(&var->_value, expected, desired, true, _z_memory_order_map[success], _z_memory_order_map[failure]); } void _z_atomic_thread_fence(_z_memory_order_t order) { __atomic_thread_fence(_z_memory_order_map[order]); } #else #error "Atomic operations in C99 only exists for GCC, use GCC or C11 or deactivate multi-thread" #endif #endif #else void _z_atomic_size_init(_z_atomic_size_t *var, size_t value) { var->_value = value; } size_t _z_atomic_size_load(_z_atomic_size_t *var, _z_memory_order_t order) { (void)order; return var->_value; } void _z_atomic_size_store(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { (void)order; var->_value = val; } size_t _z_atomic_size_fetch_add(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { (void)order; size_t old = var->_value; var->_value += val; return old; } size_t _z_atomic_size_fetch_sub(_z_atomic_size_t *var, size_t val, _z_memory_order_t order) { (void)order; size_t old = var->_value; var->_value -= val; return old; } bool _z_atomic_size_compare_exchange_strong(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { (void)success; (void)failure; if (*expected == var->_value) { var->_value = desired; return true; } *expected = var->_value; return false; } bool _z_atomic_size_compare_exchange_weak(_z_atomic_size_t *var, size_t *expected, size_t desired, _z_memory_order_t success, _z_memory_order_t failure) { (void)success; (void)failure; return _z_atomic_size_compare_exchange_strong(var, expected, desired, success, failure); } void _z_atomic_thread_fence(_z_memory_order_t order) { (void)order; // No-op in single-threaded mode } #endif ================================================ FILE: src/collections/bytes.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/bytes.h" #include #include #include #include "zenoh-pico/api/olv_macros.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/result.h" /*-------- Bytes --------*/ bool _z_bytes_check(const _z_bytes_t *bytes) { return !_z_bytes_is_empty(bytes); } z_result_t _z_bytes_copy(_z_bytes_t *dst, const _z_bytes_t *src) { return _z_arc_slice_svec_copy(&dst->_slices, &src->_slices, true); } _z_bytes_t _z_bytes_duplicate(const _z_bytes_t *src) { _z_bytes_t dst = _z_bytes_null(); _z_bytes_copy(&dst, src); return dst; } size_t _z_bytes_len(const _z_bytes_t *bs) { size_t len = 0; for (size_t i = 0; i < _z_arc_slice_svec_len(&bs->_slices); ++i) { const _z_arc_slice_t *s = _z_arc_slice_svec_get(&bs->_slices, i); len += _z_arc_slice_len(s); } return len; } bool _z_bytes_is_empty(const _z_bytes_t *bs) { for (size_t i = 0; i < _z_arc_slice_svec_len(&bs->_slices); i++) { const _z_arc_slice_t *s = _z_arc_slice_svec_get(&bs->_slices, i); if (_z_arc_slice_len(s) > 0) return false; } return true; } void _z_bytes_free(_z_bytes_t **bs) { _z_bytes_t *ptr = *bs; if (ptr != NULL) { _z_bytes_drop(ptr); z_free(ptr); *bs = NULL; } } size_t _z_bytes_to_buf(const _z_bytes_t *bytes, uint8_t *dst, size_t len) { uint8_t *start = dst; size_t remaining = len; for (size_t i = 0; i < _z_bytes_num_slices(bytes) && remaining > 0; ++i) { // Recopy data _z_arc_slice_t *s = _z_bytes_get_slice(bytes, i); size_t s_len = _z_arc_slice_len(s); size_t len_to_copy = remaining >= s_len ? s_len : remaining; memcpy(start, _z_arc_slice_data(s), len_to_copy); start += len_to_copy; remaining -= len_to_copy; } return len - remaining; } z_result_t _z_bytes_from_slice(_z_bytes_t *b, _z_slice_t *s) { *b = _z_bytes_null(); _z_arc_slice_t arc_s = _z_arc_slice_wrap(s, 0, s->len); if (_z_arc_slice_len(&arc_s) != s->len) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return _z_arc_slice_svec_append(&b->_slices, &arc_s, true); } z_result_t _z_bytes_from_buf(_z_bytes_t *b, const uint8_t *src, size_t len) { *b = _z_bytes_null(); if (len == 0) return _Z_RES_OK; _z_slice_t s = _z_slice_copy_from_buf(src, len); if (s.len != len) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return _z_bytes_from_slice(b, &s); } z_result_t _z_bytes_to_slice(const _z_bytes_t *bytes, _z_slice_t *s) { // TODO: consider return a slice with custom deleter referencing the corresponding _arc_slice // to avoid extra copy // Allocate slice size_t len = _z_bytes_len(bytes); *s = _z_slice_make(len); if (!_z_slice_check(s) && len > 0) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } uint8_t *start = (uint8_t *)s->start; for (size_t i = 0; i < _z_bytes_num_slices(bytes); ++i) { // Recopy data _z_arc_slice_t *arc_s = _z_bytes_get_slice(bytes, i); size_t s_len = _z_arc_slice_len(arc_s); memcpy(start, _z_arc_slice_data(arc_s), s_len); start += s_len; } return _Z_RES_OK; } z_result_t _z_bytes_append_slice(_z_bytes_t *dst, _z_arc_slice_t *s) { z_result_t ret = _Z_RES_OK; ret = _z_arc_slice_svec_append(&dst->_slices, s, true); if (ret != _Z_RES_OK) { _z_arc_slice_drop(s); } return ret; } z_result_t _z_bytes_append_bytes(_z_bytes_t *dst, _z_bytes_t *src) { z_result_t res = _Z_RES_OK; for (size_t i = 0; i < _z_bytes_num_slices(src); ++i) { _z_arc_slice_t s; _z_arc_slice_move(&s, _z_bytes_get_slice(src, i)); res = _z_bytes_append_slice(dst, &s); if (res != _Z_RES_OK) { break; } } _z_bytes_drop(src); return res; } _z_slice_t _z_bytes_try_get_contiguous(const _z_bytes_t *bs) { if (_z_bytes_num_slices(bs) == 1) { _z_arc_slice_t *arc_s = _z_bytes_get_slice(bs, 0); return _z_slice_alias_buf(_z_arc_slice_data(arc_s), _z_arc_slice_len(arc_s)); } return _z_slice_null(); } z_result_t _z_bytes_move(_z_bytes_t *dst, _z_bytes_t *src) { if (src->_slices._aliased) { *dst = _z_bytes_null(); _z_bytes_t csrc; _Z_RETURN_IF_ERR(_z_arc_slice_svec_copy(&csrc._slices, &src->_slices, false)); *src = csrc; } *dst = *src; *src = _z_bytes_null(); return _Z_RES_OK; } _z_bytes_reader_t _z_bytes_get_reader(const _z_bytes_t *bytes) { _z_bytes_reader_t r; r.bytes = bytes; r.slice_idx = 0; r.byte_idx = 0; r.in_slice_idx = 0; return r; } z_result_t _z_bytes_reader_seek_forward(_z_bytes_reader_t *reader, size_t offset) { size_t start_slice = reader->slice_idx; for (size_t i = start_slice; i < _z_bytes_num_slices(reader->bytes); ++i) { _z_arc_slice_t *s = _z_bytes_get_slice(reader->bytes, i); size_t remaining = _z_arc_slice_len(s) - reader->in_slice_idx; if (offset >= remaining) { reader->slice_idx += 1; reader->in_slice_idx = 0; reader->byte_idx += remaining; offset -= remaining; } else { reader->in_slice_idx += offset; reader->byte_idx += offset; offset = 0; } if (offset == 0) break; } if (offset > 0) _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); return _Z_RES_OK; } z_result_t _z_bytes_reader_seek_backward(_z_bytes_reader_t *reader, size_t offset) { while (offset != 0) { if (reader->in_slice_idx == 0) { if (reader->slice_idx == 0) _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); reader->slice_idx--; _z_arc_slice_t *s = _z_bytes_get_slice(reader->bytes, reader->slice_idx); reader->in_slice_idx = _z_arc_slice_len(s); } if (offset > reader->in_slice_idx) { offset -= reader->in_slice_idx; reader->byte_idx -= reader->in_slice_idx; reader->in_slice_idx = 0; } else { reader->byte_idx -= offset; reader->in_slice_idx -= offset; offset = 0; } } return _Z_RES_OK; } z_result_t _z_bytes_reader_seek(_z_bytes_reader_t *reader, int64_t offset, int origin) { switch (origin) { case SEEK_SET: { reader->byte_idx = 0; reader->in_slice_idx = 0; reader->slice_idx = 0; if (offset < 0) _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); return _z_bytes_reader_seek_forward(reader, (size_t)offset); } case SEEK_CUR: { if (offset >= 0) return _z_bytes_reader_seek_forward(reader, (size_t)offset); else return _z_bytes_reader_seek_backward(reader, (size_t)(-offset)); } case SEEK_END: { reader->byte_idx = _z_bytes_len(reader->bytes); reader->in_slice_idx = 0; reader->slice_idx = _z_bytes_num_slices(reader->bytes); if (offset > 0) _Z_ERROR_RETURN(_Z_ERR_DID_NOT_READ); else return _z_bytes_reader_seek_backward(reader, (size_t)(-offset)); } default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } } int64_t _z_bytes_reader_tell(const _z_bytes_reader_t *reader) { return (int64_t)reader->byte_idx; } size_t _z_bytes_reader_read(_z_bytes_reader_t *reader, uint8_t *buf, size_t len) { uint8_t *buf_start = buf; size_t to_read = len; for (size_t i = reader->slice_idx; i < _z_bytes_num_slices(reader->bytes); ++i) { _z_arc_slice_t *s = _z_bytes_get_slice(reader->bytes, i); size_t remaining = _z_arc_slice_len(s) - reader->in_slice_idx; if (len >= remaining) { memcpy(buf_start, _z_arc_slice_data(s) + reader->in_slice_idx, remaining); reader->slice_idx += 1; reader->in_slice_idx = 0; reader->byte_idx += remaining; len -= remaining; buf_start += remaining; } else { memcpy(buf_start, _z_arc_slice_data(s) + reader->in_slice_idx, len); reader->in_slice_idx += len; reader->byte_idx += len; len = 0; } if (len == 0) break; } return to_read - len; } z_result_t _z_bytes_reader_read_slices(_z_bytes_reader_t *reader, size_t len, _z_bytes_t *out) { *out = _z_bytes_null(); z_result_t res = _Z_RES_OK; for (size_t i = reader->slice_idx; i < _z_bytes_num_slices(reader->bytes) && len > 0; ++i) { _z_arc_slice_t *s = _z_bytes_get_slice(reader->bytes, i); size_t s_len = _z_arc_slice_len(s); size_t remaining = s_len - reader->in_slice_idx; size_t len_to_copy = remaining > len ? len : remaining; _z_arc_slice_t ss = _z_arc_slice_get_subslice(s, reader->in_slice_idx, len_to_copy); reader->in_slice_idx += len_to_copy; reader->byte_idx += len_to_copy; if (reader->in_slice_idx == s_len) { reader->slice_idx++; reader->in_slice_idx = 0; } if (_z_slice_simple_rc_is_null(&ss.slice)) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); res = _Z_ERR_SYSTEM_OUT_OF_MEMORY; break; } res = _z_bytes_append_slice(out, &ss); if (res != _Z_RES_OK) { _z_arc_slice_drop(&ss); break; } len -= len_to_copy; } if (len > 0 && res == _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_DID_NOT_READ); res = _Z_ERR_DID_NOT_READ; } if (res != _Z_RES_OK) { _z_bytes_drop(out); _Z_ERROR_RETURN(res); } return _Z_RES_OK; } _z_bytes_writer_t _z_bytes_writer_from_bytes(_z_bytes_t *bytes) { _z_bytes_writer_t writer; writer.cache = NULL; writer.bytes = _z_bytes_steal(bytes); return writer; } _z_bytes_writer_t _z_bytes_writer_empty(void) { _z_bytes_writer_t writer; writer.cache = NULL; writer.bytes = _z_bytes_null(); return writer; } bool _z_bytes_writer_is_empty(const _z_bytes_writer_t *writer) { return _z_bytes_is_empty(&writer->bytes); } bool _z_bytes_writer_check(const _z_bytes_writer_t *writer) { return !_z_bytes_writer_is_empty(writer); } z_result_t _z_bytes_writer_ensure_cache(_z_bytes_writer_t *writer) { assert(writer->cache != NULL); if (_z_slice_simple_rc_value(&writer->cache->slice)->len > writer->cache->len) { return _Z_RES_OK; } // otherwise we allocate a new cache _z_slice_t s = _z_slice_make(writer->cache->len * 2); if (s.start == NULL) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); _z_arc_slice_t cache = _z_arc_slice_wrap(&s, 0, 0); if (_z_slice_simple_rc_is_null(&cache.slice)) { _z_slice_clear(&s); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_CLEAN_RETURN_IF_ERR(_z_bytes_append_slice(&writer->bytes, &cache), _z_arc_slice_drop(&cache)); writer->cache = _z_bytes_get_slice(&writer->bytes, _z_bytes_num_slices(&writer->bytes) - 1); return _Z_RES_OK; } z_result_t _z_bytes_writer_init_cache(_z_bytes_writer_t *writer, const uint8_t *src, size_t len) { assert(writer->cache == NULL); _z_slice_t s = _z_slice_copy_from_buf(src, len); if (s.len != len) _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); _z_arc_slice_t arc_s = _z_arc_slice_wrap(&s, 0, len); if (_z_slice_simple_rc_is_null(&arc_s.slice)) { _z_slice_clear(&s); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _Z_RETURN_IF_ERR(_z_bytes_append_slice(&writer->bytes, &arc_s)); writer->cache = _z_bytes_get_slice(&writer->bytes, _z_bytes_num_slices(&writer->bytes) - 1); return _Z_RES_OK; } z_result_t _z_bytes_writer_write_all(_z_bytes_writer_t *writer, const uint8_t *src, size_t len) { if (writer->cache == NULL) { // no cache - append data as a single slice return _z_bytes_writer_init_cache(writer, src, len); } while (len > 0) { _Z_RETURN_IF_ERR(_z_bytes_writer_ensure_cache(writer)); size_t remaining_in_cache = _z_slice_simple_rc_value(&writer->cache->slice)->len - writer->cache->len; size_t to_copy = remaining_in_cache < len ? remaining_in_cache : len; uint8_t *buffer_start = (uint8_t *)_z_slice_simple_rc_value(&writer->cache->slice)->start + writer->cache->len; memcpy(buffer_start, src, to_copy); len -= to_copy; writer->cache->len += to_copy; src += to_copy; } return _Z_RES_OK; } z_result_t _z_bytes_writer_append_z_bytes(_z_bytes_writer_t *writer, _z_bytes_t *src) { _Z_CLEAN_RETURN_IF_ERR(_z_bytes_append_bytes(&writer->bytes, src), _z_bytes_drop(src)); writer->cache = NULL; return _Z_RES_OK; } z_result_t _z_bytes_writer_append_slice(_z_bytes_writer_t *writer, _z_arc_slice_t *src) { _Z_CLEAN_RETURN_IF_ERR(_z_bytes_append_slice(&writer->bytes, src), _z_arc_slice_drop(src)); writer->cache = NULL; return _Z_RES_OK; } _z_bytes_t _z_bytes_writer_finish(_z_bytes_writer_t *writer) { _z_bytes_t out; _z_bytes_move(&out, &writer->bytes); writer->cache = NULL; return out; } void _z_bytes_writer_clear(_z_bytes_writer_t *writer) { _z_bytes_drop(&writer->bytes); writer->cache = NULL; } z_result_t _z_bytes_writer_move(_z_bytes_writer_t *dst, _z_bytes_writer_t *src) { dst->cache = src->cache; _z_bytes_move(&dst->bytes, &src->bytes); src->cache = NULL; return _Z_RES_OK; } size_t _z_bytes_reader_remaining(const _z_bytes_reader_t *reader) { return _z_bytes_len(reader->bytes) - reader->byte_idx; } ================================================ FILE: src/collections/fifo.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/fifo.h" #include #include #include /*-------- fifo --------*/ z_result_t _z_fifo_init(_z_fifo_t *r, size_t capacity) { _z_ring_init(&r->_ring, capacity); return 0; } _z_fifo_t _z_fifo_make(size_t capacity) { _z_fifo_t v; _z_fifo_init(&v, capacity); return v; } size_t _z_fifo_capacity(const _z_fifo_t *r) { return _z_ring_capacity(&r->_ring); } size_t _z_fifo_len(const _z_fifo_t *r) { return _z_ring_len(&r->_ring); } bool _z_fifo_is_empty(const _z_fifo_t *r) { return _z_ring_is_empty(&r->_ring); } bool _z_fifo_is_full(const _z_fifo_t *r) { return _z_fifo_len(r) == _z_fifo_capacity(r); } void *_z_fifo_push(_z_fifo_t *r, void *e) { return _z_ring_push(&r->_ring, e); } void _z_fifo_push_drop(_z_fifo_t *r, void *e, z_element_free_f free_f) { void *ret = _z_fifo_push(r, e); if (ret != NULL) { free_f(&ret); } } void *_z_fifo_pull(_z_fifo_t *r) { return _z_ring_pull(&r->_ring); } void _z_fifo_clear(_z_fifo_t *r, z_element_free_f free_f) { _z_ring_clear(&r->_ring, free_f); } void _z_fifo_free(_z_fifo_t **r, z_element_free_f free_f) { _z_fifo_t *ptr = (_z_fifo_t *)*r; if (ptr != NULL) { _z_fifo_clear(ptr, free_f); z_free(ptr); *r = NULL; } } ================================================ FILE: src/collections/fifo_mt.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/fifo_mt.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" /*-------- Fifo Buffer Multithreaded --------*/ z_result_t _z_fifo_mt_init(_z_fifo_mt_t *fifo, size_t capacity) { _Z_RETURN_IF_ERR(_z_fifo_init(&fifo->_fifo, capacity)) fifo->is_closed = false; #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_init(&fifo->_mutex)) _Z_RETURN_IF_ERR(_z_condvar_init(&fifo->_cv_not_full)) _Z_RETURN_IF_ERR(_z_condvar_init(&fifo->_cv_not_empty)) #endif return _Z_RES_OK; } _z_fifo_mt_t *_z_fifo_mt_new(size_t capacity) { _z_fifo_mt_t *fifo = (_z_fifo_mt_t *)z_malloc(sizeof(_z_fifo_mt_t)); if (fifo == NULL) { _Z_ERROR("z_malloc failed"); return NULL; } z_result_t ret = _z_fifo_mt_init(fifo, capacity); if (ret != _Z_RES_OK) { _Z_ERROR("_z_fifo_mt_init failed: %i", ret); z_free(fifo); return NULL; } return fifo; } void _z_fifo_mt_clear(_z_fifo_mt_t *fifo, z_element_free_f free_f) { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&fifo->_mutex); _z_condvar_drop(&fifo->_cv_not_full); _z_condvar_drop(&fifo->_cv_not_empty); #endif _z_fifo_clear(&fifo->_fifo, free_f); } void _z_fifo_mt_free(_z_fifo_mt_t *fifo, z_element_free_f free_f) { _z_fifo_mt_clear(fifo, free_f); z_free(fifo); } z_result_t _z_fifo_mt_push(const void *elem, void *context, z_element_free_f element_free) { _ZP_UNUSED(element_free); if (elem == NULL || context == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_fifo_mt_t *f = (_z_fifo_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&f->_mutex)) while (elem != NULL) { elem = _z_fifo_push(&f->_fifo, (void *)elem); if (elem != NULL) { _Z_RETURN_IF_ERR(_z_condvar_wait(&f->_cv_not_full, &f->_mutex)) } else { _Z_RETURN_IF_ERR(_z_condvar_signal(&f->_cv_not_empty)) } } _Z_RETURN_IF_ERR(_z_mutex_unlock(&f->_mutex)) #else // Z_FEATURE_MULTI_THREAD == 1 _z_fifo_push_drop(&f->_fifo, (void *)elem, element_free); #endif // Z_FEATURE_MULTI_THREAD == 1 return _Z_RES_OK; } z_result_t _z_fifo_mt_close(_z_fifo_mt_t *fifo) { #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&fifo->_mutex)) fifo->is_closed = true; _Z_RETURN_IF_ERR(_z_condvar_signal_all(&fifo->_cv_not_empty)) _Z_RETURN_IF_ERR(_z_mutex_unlock(&fifo->_mutex)) #else fifo->is_closed = true; #endif return _Z_RES_OK; } z_result_t _z_fifo_mt_pull(void *dst, void *context, z_element_move_f element_move) { _z_fifo_mt_t *f = (_z_fifo_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 void *src = NULL; _Z_RETURN_IF_ERR(_z_mutex_lock(&f->_mutex)) while (src == NULL) { src = _z_fifo_pull(&f->_fifo); if (src == NULL) { if (f->is_closed) break; _Z_RETURN_IF_ERR(_z_condvar_wait(&f->_cv_not_empty, &f->_mutex)) } else { _Z_RETURN_IF_ERR(_z_condvar_signal(&f->_cv_not_full)) } } _Z_RETURN_IF_ERR(_z_mutex_unlock(&f->_mutex)) if (f->is_closed && src == NULL) return _Z_RES_CHANNEL_CLOSED; element_move(dst, src); #else // Z_FEATURE_MULTI_THREAD == 1 void *src = _z_fifo_pull(&f->_fifo); if (src != NULL) { element_move(dst, src); } else if (f->is_closed) { return _Z_RES_CHANNEL_CLOSED; } #endif // Z_FEATURE_MULTI_THREAD == 1 return _Z_RES_OK; } z_result_t _z_fifo_mt_try_pull(void *dst, void *context, z_element_move_f element_move) { _z_fifo_mt_t *f = (_z_fifo_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 void *src = NULL; _Z_RETURN_IF_ERR(_z_mutex_lock(&f->_mutex)) src = _z_fifo_pull(&f->_fifo); if (src != NULL) { _Z_RETURN_IF_ERR(_z_condvar_signal(&f->_cv_not_full)) } _Z_RETURN_IF_ERR(_z_mutex_unlock(&f->_mutex)) #else // Z_FEATURE_MULTI_THREAD == 1 void *src = _z_fifo_pull(&f->_fifo); #endif // Z_FEATURE_MULTI_THREAD == 1 if (src != NULL) { element_move(dst, src); } else if (f->is_closed) { return _Z_RES_CHANNEL_CLOSED; } else { return _Z_RES_CHANNEL_NODATA; } return _Z_RES_OK; } ================================================ FILE: src/collections/hashmap.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/hashmap.h" #include #include #include #include "zenoh-pico/utils/logging.h" /*-------- hashmap --------*/ void _z_hashmap_init(_z_hashmap_t *map, size_t capacity, z_element_hash_f f_hash, z_element_eq_f f_equals) { map->_capacity = capacity; map->_vals = NULL; map->_f_hash = f_hash; map->_f_equals = f_equals; } _z_hashmap_t _z_hashmap_make(size_t capacity, z_element_hash_f f_hash, z_element_eq_f f_equals) { _z_hashmap_t map; _z_hashmap_init(&map, capacity, f_hash, f_equals); return map; } size_t _z_hashmap_capacity(const _z_hashmap_t *map) { return map->_capacity; } size_t _z_hashmap_len(const _z_hashmap_t *map) { size_t len = 0; if (map->_vals != NULL) { for (size_t idx = 0; idx < map->_capacity; idx++) { len = len + _z_list_len(map->_vals[idx]); } } return len; } z_result_t _z_hashmap_copy(_z_hashmap_t *dst, const _z_hashmap_t *src, z_element_clone_f f_c) { assert((dst != NULL) && (src != NULL) && (dst->_capacity == src->_capacity)); for (size_t idx = 0; idx < src->_capacity; idx++) { const _z_list_t *src_list = src->_vals[idx]; if (src_list == NULL) { continue; } // Allocate entry dst->_vals[idx] = _z_list_clone(src_list, f_c); if (dst->_vals[idx] == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } dst->_f_hash = src->_f_hash; dst->_f_equals = src->_f_equals; return _Z_RES_OK; } _z_hashmap_t _z_hashmap_clone(const _z_hashmap_t *src, z_element_clone_f f_c, z_element_free_f f_f) { _z_hashmap_t dst = { ._capacity = src->_capacity, ._vals = NULL, ._f_hash = src->_f_hash, ._f_equals = src->_f_equals}; if (src->_vals == NULL) { return dst; } // Lazily allocate and initialize to NULL all the pointers size_t len = dst._capacity * sizeof(_z_list_t *); dst._vals = (_z_list_t **)z_malloc(len); if (dst._vals == NULL) { return dst; } (void)memset(dst._vals, 0, len); // Copy elements if (_z_hashmap_copy(&dst, src, f_c) != _Z_RES_OK) { // Free the map _z_hashmap_clear(&dst, f_f); } return dst; } bool _z_hashmap_is_empty(const _z_hashmap_t *map) { return _z_hashmap_len(map) == (size_t)0; } void _z_hashmap_remove(_z_hashmap_t *map, const void *k, z_element_free_f f) { if (map->_vals != NULL) { size_t idx = map->_f_hash(k) % map->_capacity; _z_hashmap_entry_t e; e._key = (void *)k; // k will not be mutated by this operation e._val = NULL; map->_vals[idx] = _z_list_drop_filter(map->_vals[idx], f, map->_f_equals, &e, true); } } _z_hashmap_entry_t _z_hashmap_extract(_z_hashmap_t *map, const void *key) { _z_hashmap_entry_t out; out._key = NULL; out._val = NULL; if (map->_vals != NULL) { size_t idx = map->_f_hash(key) % map->_capacity; _z_hashmap_entry_t e; e._key = (void *)key; // k will not be mutated by this operation e._val = NULL; _z_list_t *extracted; map->_vals[idx] = _z_list_extract_filter(map->_vals[idx], map->_f_equals, &e, &extracted, true); if (extracted != NULL) { _z_hashmap_entry_t *kv = (_z_hashmap_entry_t *)((extracted)->_val); out._key = kv->_key; out._val = kv->_val; z_free(kv); z_free(extracted); } } return out; } void *_z_hashmap_insert(_z_hashmap_t *map, void *k, void *v, z_element_free_f f_f, bool replace) { if (map->_vals == NULL) { // Lazily allocate and initialize to NULL all the pointers size_t len = map->_capacity * sizeof(_z_list_t *); map->_vals = (_z_list_t **)z_malloc(len); if (map->_vals != NULL) { (void)memset(map->_vals, 0, len); } else { return NULL; } } if (replace) { // Free any old value _z_hashmap_remove(map, k, f_f); } // Insert the element _z_hashmap_entry_t *entry = (_z_hashmap_entry_t *)z_malloc(sizeof(_z_hashmap_entry_t)); if (entry != NULL) { entry->_key = k; entry->_val = v; size_t idx = map->_f_hash(k) % map->_capacity; map->_vals[idx] = _z_list_push(map->_vals[idx], entry); } else { return NULL; } return v; } void *_z_hashmap_get(const _z_hashmap_t *map, const void *k) { void *ret = NULL; if (map->_vals != NULL) { size_t idx = map->_f_hash(k) % map->_capacity; _z_hashmap_entry_t e; e._key = (void *)k; // k will not be mutated by this operation e._val = NULL; _z_list_t *xs = _z_list_find(map->_vals[idx], map->_f_equals, &e); if (xs != NULL) { _z_hashmap_entry_t *h = (_z_hashmap_entry_t *)_z_list_value(xs); ret = h->_val; } } return ret; } _z_list_t *_z_hashmap_get_all(const _z_hashmap_t *map, const void *k) { if (map->_vals != NULL) { size_t idx = map->_f_hash(k) % map->_capacity; _z_hashmap_entry_t e; e._key = (void *)k; // k will not be mutated by this operation e._val = NULL; return _z_list_find(map->_vals[idx], map->_f_equals, &e); } return NULL; } _z_hashmap_iterator_t _z_hashmap_iterator_make(const _z_hashmap_t *map) { _z_hashmap_iterator_t iter = {0}; iter._map = map; return iter; } bool _z_hashmap_iterator_next(_z_hashmap_iterator_t *iter) { if (iter->_map->_vals == NULL) { return false; } while (iter->_idx < iter->_map->_capacity) { if (iter->_list_ptr == NULL) { iter->_list_ptr = iter->_map->_vals[iter->_idx]; } else { iter->_list_ptr = _z_list_next(iter->_list_ptr); } if (iter->_list_ptr == NULL) { iter->_idx++; continue; } iter->_entry = iter->_list_ptr->_val; return true; } return false; } void *_z_hashmap_iterator_key(const _z_hashmap_iterator_t *iter) { return iter->_entry->_key; } void *_z_hashmap_iterator_value(const _z_hashmap_iterator_t *iter) { return iter->_entry->_val; } void _z_hashmap_clear(_z_hashmap_t *map, z_element_free_f f_f) { if (map->_vals != NULL) { for (size_t idx = 0; idx < map->_capacity; idx++) { _z_list_free(&map->_vals[idx], f_f); } z_free(map->_vals); map->_vals = NULL; } } void _z_hashmap_free(_z_hashmap_t **map, z_element_free_f f) { _z_hashmap_t *ptr = *map; if (ptr != NULL) { _z_hashmap_clear(ptr, f); z_free(ptr); *map = NULL; } } ================================================ FILE: src/collections/lifo.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/lifo.h" #include #include #include /*-------- lifo --------*/ z_result_t _z_lifo_init(_z_lifo_t *r, size_t capacity) { memset(r, 0, sizeof(_z_lifo_t)); if (capacity != (size_t)0) { r->_val = (void **)z_malloc(sizeof(void *) * capacity); } if (r->_val != NULL) { memset(r->_val, 0, capacity); r->_capacity = capacity; } return 0; } _z_lifo_t _z_lifo_make(size_t capacity) { _z_lifo_t v; _z_lifo_init(&v, capacity); return v; } size_t _z_lifo_capacity(const _z_lifo_t *r) { return r->_capacity; } size_t _z_lifo_len(const _z_lifo_t *r) { return r->_len; } bool _z_lifo_is_empty(const _z_lifo_t *r) { return r->_len == 0; } bool _z_lifo_is_full(const _z_lifo_t *r) { return r->_len == r->_capacity; } void *_z_lifo_push(_z_lifo_t *r, void *e) { void *ret = e; if (!_z_lifo_is_full(r)) { r->_val[r->_len] = e; r->_len++; ret = NULL; } return ret; } void _z_lifo_push_drop(_z_lifo_t *r, void *e, z_element_free_f free_f) { void *ret = _z_lifo_push(r, e); if (ret != NULL) { free_f(&ret); } } void *_z_lifo_pull(_z_lifo_t *r) { void *ret = NULL; if (!_z_lifo_is_empty(r)) { r->_len--; ret = r->_val[r->_len]; } return ret; } void _z_lifo_clear(_z_lifo_t *r, z_element_free_f free_f) { void *e = _z_lifo_pull(r); while (e != NULL) { free_f(&e); e = _z_lifo_pull(r); } z_free(r->_val); r->_val = NULL; r->_capacity = (size_t)0; r->_len = (size_t)0; } void _z_lifo_free(_z_lifo_t **r, z_element_free_f free_f) { _z_lifo_t *ptr = (_z_lifo_t *)*r; if (ptr != NULL) { _z_lifo_clear(ptr, free_f); z_free(ptr); *r = NULL; } } ================================================ FILE: src/collections/list.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/list.h" #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" /*-------- Inner single-linked list --------*/ static _z_list_t *_z_list_new(void *x) { _z_list_t *xs = (_z_list_t *)z_malloc(sizeof(_z_list_t)); if (xs == NULL) { _Z_ERROR("Failed to allocate list element."); return NULL; } xs->_val = x; xs->_next = NULL; return xs; } _z_list_t *_z_list_push(_z_list_t *xs, void *x) { _z_list_t *lst = _z_list_new(x); if (lst == NULL) { return xs; } lst->_next = xs; return lst; } _z_list_t *_z_list_push_after(_z_list_t *xs, void *x) { _z_list_t *l = _z_list_new(x); if (l == NULL || xs == NULL) { return l; } l->_next = xs->_next; xs->_next = l; return xs; } _z_list_t *_z_list_push_back(_z_list_t *xs, void *x) { if (xs == NULL) { return _z_list_new(x); } _z_list_t *l = xs; while (l->_next != NULL) { l = l->_next; } l->_next = _z_list_new(x); return xs; } _z_list_t *_z_list_push_sorted(_z_list_t *xs, z_element_cmp_f c_f, void *x) { if (xs == NULL) { return _z_list_new(x); } _z_list_t *l = xs; _z_list_t *prev = NULL; while (l != NULL && c_f(l->_val, x) <= 0) { prev = l; l = l->_next; } _z_list_t *new_elem = _z_list_new(x); if (new_elem == NULL) { return xs; } if (prev == NULL) { new_elem->_next = xs; return new_elem; } else { new_elem->_next = l; prev->_next = new_elem; return xs; } } size_t _z_list_len(const _z_list_t *xs) { size_t len = 0; _z_list_t *l = (_z_list_t *)xs; while (l != NULL) { len = len + (size_t)1; l = _z_list_next(l); } return len; } _z_list_t *_z_list_pop(_z_list_t *xs, z_element_free_f f_f, void **x) { if (xs == NULL) { return xs; } _z_list_t *head = xs; _z_list_t *l = head->_next; if (x != NULL) { *x = head->_val; } else { f_f(&head->_val); } z_free(head); return l; } _z_list_t *_z_list_find(const _z_list_t *xs, z_element_eq_f c_f, const void *e) { _z_list_t *l = (_z_list_t *)xs; _z_list_t *ret = NULL; while (l != NULL) { void *head = _z_list_value(l); if (c_f(e, head)) { ret = l; break; } l = _z_list_next(l); } return ret; } _z_list_t *_z_list_drop_element(_z_list_t *list, _z_list_t *prev, z_element_free_f f_f) { _z_list_t *dropped = NULL; if (prev == NULL) { // Head removal dropped = list; list = list->_next; } else { // Other cases dropped = prev->_next; if (dropped != NULL) { prev->_next = dropped->_next; } } if (dropped != NULL) { f_f(&dropped->_val); z_free(dropped); } return list; } _z_list_t *_z_list_drop_filter(_z_list_t *xs, z_element_free_f f_f, z_element_eq_f c_f, const void *left, bool only_first) { _z_list_t *l = (_z_list_t *)xs; _z_list_t *previous = xs; _z_list_t *current = xs; while (current != NULL) { if (c_f(left, current->_val)) { _z_list_t *next = current->_next; _z_list_t *this_ = current; // head removal if (this_ == l) { l = l->_next; } // tail removal else if (this_->_next == NULL) { previous->_next = NULL; } // middle removal else { previous->_next = this_->_next; } f_f(&this_->_val); z_free(this_); if (only_first) { break; } current = next; } else { previous = current; current = current->_next; } } return l; } _z_list_t *_z_list_extract_filter(_z_list_t *head, z_element_eq_f c_f, const void *left, _z_list_t **extracted, bool only_first) { _z_list_t self_head = {0}; _z_list_t extracted_head = {0}; _z_list_t *self_tail = &self_head; _z_list_t *extracted_tail = &extracted_head; _z_list_t *current = head; while (current != NULL) { _z_list_t *next = current->_next; current->_next = NULL; if (c_f(left, current->_val)) { extracted_tail->_next = current; extracted_tail = extracted_tail->_next; if (only_first) { self_tail->_next = next; break; } } else { self_tail->_next = current; self_tail = self_tail->_next; } current = next; } *extracted = extracted_head._next; return self_head._next; } _z_list_t *_z_list_clone(const _z_list_t *xs, z_element_clone_f d_f) { _z_list_t *new = NULL; const _z_list_t *curr = xs; while (curr != NULL) { void *x = d_f(_z_list_value(curr)); new = _z_list_push_back(new, x); curr = _z_list_next(curr); } return new; } /** * Free the list in deep. This function frees * the inner void * to the element of the list. */ void _z_list_free(_z_list_t **xs, z_element_free_f f) { _z_list_t *ptr = *xs; while (ptr != NULL) { ptr = _z_list_pop(ptr, f, NULL); } *xs = NULL; } /*-------- Inner sized single-linked list --------*/ typedef struct _z_slist_node_data_t { _z_slist_t *next; } _z_slist_node_data_t; #define NODE_DATA_SIZE sizeof(_z_slist_node_data_t) static inline _z_slist_node_data_t *_z_slist_node_data(const _z_slist_t *list) { return (_z_slist_node_data_t *)list; } static inline void *_z_slist_node_value(const _z_slist_t *node) { return (void *)_z_ptr_u8_offset((uint8_t *)node, (ptrdiff_t)NODE_DATA_SIZE); } static _z_slist_t *_z_slist_new(const void *value, size_t value_size, z_element_copy_f d_f, bool use_elem_f) { size_t node_size = NODE_DATA_SIZE + value_size; _z_slist_t *node = (_z_slist_t *)z_malloc(node_size); if (node == NULL) { _Z_ERROR("Failed to allocate list element."); return node; } memset(node, 0, NODE_DATA_SIZE); if (use_elem_f) { d_f(_z_slist_node_value(node), value); } else { memcpy(_z_slist_node_value(node), value, value_size); } return node; } static _z_slist_t *_z_slist_new_empty(size_t value_size) { size_t node_size = NODE_DATA_SIZE + value_size; _z_slist_t *node = (_z_slist_t *)z_malloc(node_size); if (node == NULL) { _Z_ERROR("Failed to allocate list element."); return node; } memset(node, 0, NODE_DATA_SIZE); return node; } _z_slist_t *_z_slist_push_empty(_z_slist_t *node, size_t value_size) { _z_slist_t *new_node = _z_slist_new_empty(value_size); if (new_node == NULL) { return node; } _z_slist_node_data_t *node_data = _z_slist_node_data(new_node); node_data->next = node; return new_node; } _z_slist_t *_z_slist_push(_z_slist_t *node, const void *value, size_t value_size, z_element_copy_f d_f, bool use_elem_f) { _z_slist_t *new_node = _z_slist_new(value, value_size, d_f, use_elem_f); if (new_node == NULL) { return node; } _z_slist_node_data_t *node_data = _z_slist_node_data(new_node); node_data->next = node; return new_node; } _z_slist_t *_z_slist_push_back(_z_slist_t *node, const void *value, size_t value_size, z_element_copy_f d_f, bool use_elem_f) { if (node == NULL) { return _z_slist_new(value, value_size, d_f, use_elem_f); } _z_slist_node_data_t *node_data = _z_slist_node_data(node); while (node_data->next != NULL) { node_data = _z_slist_node_data(node_data->next); } node_data->next = _z_slist_new(value, value_size, d_f, use_elem_f); return node; } void *_z_slist_value(const _z_slist_t *node) { return _z_slist_node_value(node); } _z_slist_t *_z_slist_next(const _z_slist_t *node) { _z_slist_node_data_t *node_data = _z_slist_node_data(node); return _z_slist_node_data(node_data)->next; } size_t _z_slist_len(const _z_slist_t *node) { size_t len = 0; _z_slist_node_data_t *node_data = _z_slist_node_data(node); while (node_data != NULL) { len += (size_t)1; node_data = _z_slist_node_data(node_data->next); } return len; } _z_slist_t *_z_slist_pop(_z_slist_t *node, z_element_clear_f f_f) { if (node == NULL) { return node; } _z_slist_t *next_node = _z_slist_node_data(node)->next; f_f(_z_slist_node_value(node)); z_free(node); return next_node; } _z_slist_t *_z_slist_find(const _z_slist_t *node, z_element_eq_f c_f, const void *target_val) { _z_slist_t *curr_node = (_z_slist_t *)node; _z_slist_node_data_t *node_data = _z_slist_node_data(curr_node); while (node_data != NULL) { if (c_f(target_val, _z_slist_node_value(curr_node))) { return curr_node; } curr_node = node_data->next; node_data = _z_slist_node_data(curr_node); } return NULL; } _z_slist_t *_z_slist_drop_element(_z_slist_t *list, _z_slist_t *prev, z_element_clear_f f_f) { _z_slist_t *dropped = NULL; if (prev == NULL) { // Head removal dropped = list; list = _z_slist_node_data(list)->next; } else { // Other cases dropped = _z_slist_node_data(prev)->next; if (dropped != NULL) { _z_slist_node_data(prev)->next = _z_slist_node_data(dropped)->next; } } if (dropped != NULL) { f_f(_z_slist_node_value(dropped)); z_free(dropped); } return list; } _z_slist_t *_z_slist_drop_filter(_z_slist_t *head, z_element_clear_f f_f, z_element_eq_f c_f, const void *target_val, bool only_first) { _z_slist_t *previous = head; _z_slist_t *current = head; while (current != NULL) { if (c_f(target_val, _z_slist_node_value(current))) { if (current == head) { // head removal head = _z_slist_node_data(head)->next; } else if (_z_slist_node_data(current)->next == NULL) { // tail removal _z_slist_node_data(previous)->next = NULL; } else { // middle removal _z_slist_node_data(previous)->next = _z_slist_node_data(current)->next; } _z_slist_t *next = _z_slist_node_data(current)->next; f_f(_z_slist_node_value(current)); z_free(current); if (only_first) { break; } current = next; } else { previous = current; current = _z_slist_node_data(current)->next; } } return head; } _z_slist_t *_z_slist_extract_filter(_z_slist_t *head, z_element_eq_f c_f, const void *target_val, _z_slist_t **extracted, bool only_first) { _z_slist_node_data_t self_head = {0}; _z_slist_node_data_t extracted_head = {0}; _z_slist_node_data_t *self_tail = &self_head; _z_slist_node_data_t *extracted_tail = &extracted_head; _z_slist_t *current = head; while (current != NULL) { _z_slist_t *next = _z_slist_node_data(current)->next; _z_slist_node_data(current)->next = NULL; if (c_f(target_val, _z_slist_node_value(current))) { extracted_tail->next = current; extracted_tail = _z_slist_node_data(extracted_tail->next); if (only_first) { self_tail->next = next; break; } } else { self_tail->next = current; self_tail = _z_slist_node_data(self_tail->next); } current = next; } *extracted = extracted_head.next; return self_head.next; } _z_slist_t *_z_slist_clone(const _z_slist_t *node, size_t value_size, z_element_copy_f d_f, bool use_elem_f) { _z_slist_t *new_node = NULL; _z_slist_t *curr_node = (_z_slist_t *)node; while (curr_node != NULL) { void *value = _z_slist_node_value(curr_node); new_node = _z_slist_push(new_node, value, value_size, d_f, use_elem_f); curr_node = _z_slist_node_data(curr_node)->next; } return new_node; } void _z_slist_free(_z_slist_t **node, z_element_clear_f f) { _z_slist_t *ptr = *node; while (ptr != NULL) { ptr = _z_slist_pop(ptr, f); } *node = NULL; } ================================================ FILE: src/collections/lru_cache.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/lru_cache.h" #include #include #include #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" // Nodes are chained as double linked list for lru insertion/deletion. typedef struct _z_lru_cache_node_data_t { _z_lru_cache_node_t *prev; // List previous node _z_lru_cache_node_t *next; // List next node } _z_lru_cache_node_data_t; #define NODE_DATA_SIZE sizeof(_z_lru_cache_node_data_t) // Generic static functions static inline _z_lru_cache_t _z_lru_cache_null(void) { return (_z_lru_cache_t){0}; } static inline _z_lru_cache_node_data_t *_z_lru_cache_node_data(_z_lru_cache_node_t *node) { return (_z_lru_cache_node_data_t *)node; } static inline void *_z_lru_cache_node_value(_z_lru_cache_node_t *node) { return (void *)_z_ptr_u8_offset((uint8_t *)node, (ptrdiff_t)NODE_DATA_SIZE); } static _z_lru_cache_node_t *_z_lru_cache_node_create(void *value, size_t value_size) { size_t node_size = NODE_DATA_SIZE + value_size; _z_lru_cache_node_t *node = (_z_lru_cache_node_t *)z_malloc(node_size); if (node == NULL) { return node; } memset(node, 0, NODE_DATA_SIZE); memcpy(_z_lru_cache_node_value(node), value, value_size); return node; } // List functions static void _z_lru_cache_insert_list_node(_z_lru_cache_t *cache, _z_lru_cache_node_t *node) { _z_lru_cache_node_data_t *node_data = _z_lru_cache_node_data(node); node_data->prev = NULL; node_data->next = cache->head; if (cache->head != NULL) { _z_lru_cache_node_data_t *head_data = _z_lru_cache_node_data(cache->head); head_data->prev = node; } cache->head = node; if (cache->tail == NULL) { cache->tail = node; } } static void _z_lru_cache_remove_list_node(_z_lru_cache_t *cache, _z_lru_cache_node_t *node) { _z_lru_cache_node_data_t *node_data = _z_lru_cache_node_data(node); // Nominal case if ((node_data->prev != NULL) && (node_data->next != NULL)) { _z_lru_cache_node_data_t *prev_data = _z_lru_cache_node_data(node_data->prev); _z_lru_cache_node_data_t *next_data = _z_lru_cache_node_data(node_data->next); prev_data->next = node_data->next; next_data->prev = node_data->prev; } if (node_data->prev == NULL) { assert(cache->head == node); cache->head = node_data->next; if (node_data->next != NULL) { _z_lru_cache_node_data_t *next_data = _z_lru_cache_node_data(node_data->next); next_data->prev = NULL; } } if (node_data->next == NULL) { assert(cache->tail == node); cache->tail = node_data->prev; if (node_data->prev != NULL) { _z_lru_cache_node_data_t *prev_data = _z_lru_cache_node_data(node_data->prev); prev_data->next = NULL; } } } static void _z_lru_cache_update_list(_z_lru_cache_t *cache, _z_lru_cache_node_t *node) { _z_lru_cache_remove_list_node(cache, node); _z_lru_cache_insert_list_node(cache, node); } static void _z_lru_cache_clear_list(_z_lru_cache_t *cache, z_element_clear_f clear) { _z_lru_cache_node_data_t *node = cache->head; while (node != NULL) { _z_lru_cache_node_t *tmp = node; _z_lru_cache_node_data_t *node_data = _z_lru_cache_node_data(node); void *node_value = _z_lru_cache_node_value(node); node = node_data->next; clear(node_value); z_free(tmp); } } // Sorted list function static _z_lru_cache_node_t *_z_lru_cache_search_slist(_z_lru_cache_t *cache, void *value, _z_lru_val_cmp_f compare, size_t *idx) { int l_idx = 0; int h_idx = (int)cache->len - 1; while (l_idx <= h_idx) { int curr_idx = (l_idx + h_idx) / 2; int res = compare(_z_lru_cache_node_value(cache->slist[curr_idx]), value); if (res == 0) { *idx = (size_t)curr_idx; return cache->slist[curr_idx]; } else if (res < 0) { l_idx = curr_idx + 1; } else { h_idx = curr_idx - 1; } } return NULL; } static int _z_lru_cache_find_position(_z_lru_cache_node_t **slist, _z_lru_val_cmp_f compare, void *node_val, size_t slist_size) { int start = 0; int end = (int)slist_size - 1; while (start <= end) { int mid = start + (end - start) / 2; if (compare(_z_lru_cache_node_value(slist[mid]), node_val) < 0) { start = mid + 1; } else { end = mid - 1; } } return start; } static void _z_lru_cache_move_elem_slist(_z_lru_cache_t *cache, size_t *add_idx_addr, size_t *del_idx_addr) { size_t del_idx = (del_idx_addr == NULL) ? cache->len : *del_idx_addr; size_t add_idx = *add_idx_addr; if (add_idx == del_idx) { return; } // Move elements between the indices on the right if (del_idx >= add_idx) { memmove(&cache->slist[add_idx + 1], &cache->slist[add_idx], (del_idx - add_idx) * sizeof(_z_lru_cache_node_t *)); } else { // Move them on the left // Rightmost value doesn't move unless we have a new maximum if (add_idx != cache->capacity - 1) { *add_idx_addr -= 1; add_idx -= 1; } memmove(&cache->slist[del_idx], &cache->slist[del_idx + 1], (add_idx - del_idx) * sizeof(_z_lru_cache_node_t *)); } } static void _z_lru_cache_insert_slist(_z_lru_cache_t *cache, _z_lru_cache_node_t *node, _z_lru_val_cmp_f compare, size_t *del_idx) { // Find insert position: if (cache->len == 0) { cache->slist[0] = node; return; } void *node_val = _z_lru_cache_node_value(node); size_t pos = (size_t)_z_lru_cache_find_position(cache->slist, compare, node_val, cache->len); // Move elements _z_lru_cache_move_elem_slist(cache, &pos, del_idx); // Store element cache->slist[pos] = node; } static size_t _z_lru_cache_delete_slist(_z_lru_cache_t *cache, _z_lru_cache_node_t *node, _z_lru_val_cmp_f compare) { size_t del_idx = 0; // Don't delete, return the index (void)_z_lru_cache_search_slist(cache, _z_lru_cache_node_value(node), compare, &del_idx); return del_idx; } // Main static functions static size_t _z_lru_cache_delete_last(_z_lru_cache_t *cache, _z_lru_val_cmp_f compare) { _z_lru_cache_node_t *last = cache->tail; assert(last != NULL); _z_lru_cache_remove_list_node(cache, last); size_t del_idx = _z_lru_cache_delete_slist(cache, last, compare); z_free(last); cache->len--; return del_idx; } static void _z_lru_cache_insert_node(_z_lru_cache_t *cache, _z_lru_cache_node_t *node, _z_lru_val_cmp_f compare, size_t *del_idx) { _z_lru_cache_insert_list_node(cache, node); _z_lru_cache_insert_slist(cache, node, compare, del_idx); cache->len++; } static _z_lru_cache_node_t *_z_lru_cache_search_node(_z_lru_cache_t *cache, void *value, _z_lru_val_cmp_f compare) { size_t idx = 0; return _z_lru_cache_search_slist(cache, value, compare, &idx); } // Public functions _z_lru_cache_t _z_lru_cache_init(size_t capacity) { _z_lru_cache_t cache = _z_lru_cache_null(); cache.capacity = capacity; return cache; } void *_z_lru_cache_get(_z_lru_cache_t *cache, void *value, _z_lru_val_cmp_f compare) { // Lookup if node exists. _z_lru_cache_node_t *node = _z_lru_cache_search_node(cache, value, compare); if (node == NULL) { return NULL; } // Update list with node as most recent _z_lru_cache_update_list(cache, node); return _z_lru_cache_node_value(node); } z_result_t _z_lru_cache_insert(_z_lru_cache_t *cache, void *value, size_t value_size, _z_lru_val_cmp_f compare) { assert(cache->capacity > 0); // Init slist if (cache->slist == NULL) { cache->slist = (_z_lru_cache_node_t **)z_malloc(cache->capacity * sizeof(void *)); if (cache->slist == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } memset(cache->slist, 0, cache->capacity * sizeof(void *)); } // Create node _z_lru_cache_node_t *node = _z_lru_cache_node_create(value, value_size); size_t *del_idx_addr = NULL; size_t del_idx = 0; if (node == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Check capacity if (cache->len == cache->capacity) { // Delete lru entry del_idx = _z_lru_cache_delete_last(cache, compare); del_idx_addr = &del_idx; } // Update the cache _z_lru_cache_insert_node(cache, node, compare, del_idx_addr); return _Z_RES_OK; } void _z_lru_cache_clear(_z_lru_cache_t *cache, z_element_clear_f clear) { // Reset slist if (cache->slist != NULL) { memset(cache->slist, 0, cache->capacity * sizeof(void *)); } // Clear list _z_lru_cache_clear_list(cache, clear); // Reset cache cache->len = 0; cache->head = NULL; cache->tail = NULL; } void _z_lru_cache_delete(_z_lru_cache_t *cache, z_element_clear_f clear) { _z_lru_cache_clear(cache, clear); z_free(cache->slist); cache->slist = NULL; } ================================================ FILE: src/collections/refcount.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/refcount.h" #include #include "zenoh-pico/collections/atomic.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #define _Z_RC_MAX_COUNT INT32_MAX // Based on Rust lazy overflow check typedef struct { _z_atomic_size_t _strong_cnt; _z_atomic_size_t _weak_cnt; } _z_inner_rc_t; static inline _z_inner_rc_t* _z_rc_inner(void* rc) { return (_z_inner_rc_t*)rc; } z_result_t _z_rc_init(void** cnt) { *cnt = z_malloc(sizeof(_z_inner_rc_t)); if ((*cnt) == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_inner_rc_t* rc = *cnt; _z_atomic_size_init(&rc->_strong_cnt, 1); _z_atomic_size_init(&rc->_weak_cnt, 1); // Note we increase weak count by 1 when creating a new rc, to take ownership of counter. return _Z_RES_OK; } z_result_t _z_rc_increase_strong(void* cnt) { _z_inner_rc_t* c = (_z_inner_rc_t*)cnt; if (c == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (_z_atomic_size_fetch_add(&c->_strong_cnt, 1, _z_memory_order_relaxed) >= _Z_RC_MAX_COUNT) { _Z_ERROR("Rc strong count overflow"); _Z_ERROR_RETURN(_Z_ERR_OVERFLOW); } return _Z_RES_OK; } z_result_t _z_rc_increase_weak(void* cnt) { _z_inner_rc_t* c = (_z_inner_rc_t*)cnt; if (c == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (_z_atomic_size_fetch_add(&c->_weak_cnt, 1, _z_memory_order_relaxed) >= _Z_RC_MAX_COUNT) { _Z_ERROR("Rc weak count overflow"); _Z_ERROR_RETURN(_Z_ERR_OVERFLOW); } return _Z_RES_OK; } bool _z_rc_decrease_strong(void** cnt) { _z_inner_rc_t* c = (_z_inner_rc_t*)*cnt; if (_z_atomic_size_fetch_sub(&c->_strong_cnt, 1, _z_memory_order_release) > 1) { return false; } // destroy fake weak that we created during strong init _z_rc_decrease_weak(cnt); return true; } bool _z_rc_decrease_weak(void** cnt) { _z_inner_rc_t* c = (_z_inner_rc_t*)*cnt; if (_z_atomic_size_fetch_sub(&c->_weak_cnt, 1, _z_memory_order_release) > 1) { return false; } _z_atomic_thread_fence( _z_memory_order_acquire); // ensure we see the latest state of strong count before we free the counter z_free(*cnt); *cnt = NULL; return true; } z_result_t _z_rc_weak_upgrade(void* cnt) { if (cnt == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _z_inner_rc_t* c = (_z_inner_rc_t*)cnt; size_t prev = _z_atomic_size_load(&c->_strong_cnt, _z_memory_order_relaxed); while ((prev != 0) && (prev < _Z_RC_MAX_COUNT)) { if (_z_atomic_size_compare_exchange_weak(&c->_strong_cnt, &prev, prev + 1, _z_memory_order_acquire, _z_memory_order_relaxed)) { return _Z_RES_OK; } } _Z_ERROR_RETURN(_Z_ERR_INVALID); } size_t _z_rc_weak_count(void* rc) { if (rc == NULL) { return 0; } size_t strong_count = _z_atomic_size_load(&_z_rc_inner(rc)->_strong_cnt, _z_memory_order_relaxed); size_t weak_count = _z_atomic_size_load(&_z_rc_inner(rc)->_weak_cnt, _z_memory_order_relaxed); if (weak_count == 0) { return 0; } return (strong_count > 0) ? weak_count - 1 : weak_count; // substruct 1 weak ref that we added during strong init } size_t _z_rc_strong_count(void* rc) { return rc == NULL ? 0 : _z_atomic_size_load(&_z_rc_inner(rc)->_strong_cnt, _z_memory_order_relaxed); } typedef struct { _z_atomic_size_t _strong_cnt; } _z_inner_simple_rc_t; #define RC_CNT_SIZE sizeof(_z_inner_simple_rc_t) static inline _z_inner_simple_rc_t* _z_simple_rc_inner(void* rc) { return (_z_inner_simple_rc_t*)rc; } void* _z_simple_rc_value(void* rc) { return (void*)_z_ptr_u8_offset((uint8_t*)rc, (ptrdiff_t)RC_CNT_SIZE); } z_result_t _z_simple_rc_init(void** rc, const void* val, size_t val_size) { *rc = z_malloc(RC_CNT_SIZE + val_size); if ((*rc) == NULL) { _Z_ERROR("Failed to allocate rc"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_inner_simple_rc_t* inner = _z_simple_rc_inner(*rc); _z_atomic_size_init(&inner->_strong_cnt, 1); memcpy(_z_simple_rc_value(*rc), val, val_size); return _Z_RES_OK; } z_result_t _z_simple_rc_increase(void* rc) { _z_inner_simple_rc_t* c = _z_simple_rc_inner(rc); if (_z_atomic_size_fetch_add(&c->_strong_cnt, 1, _z_memory_order_relaxed) >= _Z_RC_MAX_COUNT) { _Z_ERROR("Rc strong count overflow"); _Z_ERROR_RETURN(_Z_ERR_OVERFLOW); } return _Z_RES_OK; } bool _z_simple_rc_decrease(void* rc) { _z_inner_simple_rc_t* c = _z_simple_rc_inner(rc); if (_z_atomic_size_fetch_sub(&c->_strong_cnt, 1, _z_memory_order_release) > 1) { return false; } return true; } size_t _z_simple_rc_strong_count(void* rc) { return rc == NULL ? 0 : _z_atomic_size_load(&(_z_simple_rc_inner(rc)->_strong_cnt), _z_memory_order_relaxed); } ================================================ FILE: src/collections/ring.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/ring.h" #include #include #include /*-------- ring --------*/ z_result_t _z_ring_init(_z_ring_t *r, size_t capacity) { // We need one more element to differentiate wether the ring is empty or full capacity++; memset(r, 0, sizeof(_z_ring_t)); if (capacity != (size_t)0) { r->_val = (void **)z_malloc(sizeof(void *) * capacity); } if (r->_val != NULL) { memset(r->_val, 0, capacity); r->_capacity = capacity; } return 0; } _z_ring_t _z_ring_make(size_t capacity) { _z_ring_t v; _z_ring_init(&v, capacity); return v; } size_t _z_ring_capacity(const _z_ring_t *r) { return r->_capacity - (size_t)1; } size_t _z_ring_len(const _z_ring_t *r) { if (r->_w_idx >= r->_r_idx) { return r->_w_idx - r->_r_idx; } else { return r->_w_idx + (r->_capacity - r->_r_idx); } } bool _z_ring_is_empty(const _z_ring_t *r) { return r->_w_idx == r->_r_idx; } bool _z_ring_is_full(const _z_ring_t *r) { return _z_ring_len(r) == _z_ring_capacity(r); } void *_z_ring_push(_z_ring_t *r, void *e) { void *ret = e; if (!_z_ring_is_full(r)) { r->_val[r->_w_idx] = e; r->_w_idx = (r->_w_idx + (size_t)1) % r->_capacity; ret = NULL; } return ret; } void *_z_ring_push_force(_z_ring_t *r, void *e) { void *ret = _z_ring_push(r, e); if (ret != NULL) { ret = _z_ring_pull(r); _z_ring_push(r, e); } return ret; } void _z_ring_push_force_drop(_z_ring_t *r, void *e, z_element_free_f free_f) { void *ret = _z_ring_push_force(r, e); if (ret != NULL) { free_f(&ret); } } void *_z_ring_pull(_z_ring_t *r) { void *ret = NULL; if (!_z_ring_is_empty(r)) { ret = r->_val[r->_r_idx]; r->_val[r->_r_idx] = NULL; r->_r_idx = (r->_r_idx + (size_t)1) % r->_capacity; } return ret; } void _z_ring_clear(_z_ring_t *r, z_element_free_f free_f) { void *e = _z_ring_pull(r); while (e != NULL) { free_f(&e); e = _z_ring_pull(r); } z_free(r->_val); r->_val = NULL; r->_capacity = (size_t)0; r->_len = (size_t)0; r->_r_idx = (size_t)0; r->_w_idx = (size_t)0; } void _z_ring_free(_z_ring_t **r, z_element_free_f free_f) { _z_ring_t *ptr = (_z_ring_t *)*r; if (ptr != NULL) { _z_ring_clear(ptr, free_f); z_free(ptr); *r = NULL; } } _z_ring_iterator_t _z_ring_iterator_make(const _z_ring_t *ring) { _z_ring_iterator_t iter = {0}; iter._ring = ring; iter._r_idx = ring->_r_idx; iter._w_idx = ring->_w_idx; return iter; } bool _z_ring_iterator_next(_z_ring_iterator_t *iter) { if (iter->_r_idx != iter->_w_idx) { iter->_val = iter->_ring->_val[iter->_r_idx]; iter->_r_idx = (iter->_r_idx + (size_t)1) % iter->_ring->_capacity; return true; } return false; } void *_z_ring_iterator_value(const _z_ring_iterator_t *iter) { return iter->_val; } _z_ring_reverse_iterator_t _z_ring_reverse_iterator_make(const _z_ring_t *ring) { _z_ring_reverse_iterator_t iter = {0}; iter._ring = ring; iter._r_idx = (ring->_w_idx == 0) ? ring->_capacity - 1 : ring->_w_idx - 1; iter._w_idx = (ring->_r_idx == 0) ? ring->_capacity - 1 : ring->_r_idx - 1; return iter; } bool _z_ring_reverse_iterator_next(_z_ring_reverse_iterator_t *iter) { if (iter->_r_idx != iter->_w_idx) { iter->_val = iter->_ring->_val[iter->_r_idx]; iter->_r_idx = (iter->_r_idx == 0) ? iter->_ring->_capacity - 1 : iter->_r_idx - 1; return true; } return false; } void *_z_ring_reverse_iterator_value(const _z_ring_reverse_iterator_t *iter) { return iter->_val; } ================================================ FILE: src/collections/ring_mt.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/ring_mt.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/utils/logging.h" /*-------- Ring Buffer Multithreaded --------*/ z_result_t _z_ring_mt_init(_z_ring_mt_t *ring, size_t capacity) { _Z_RETURN_IF_ERR(_z_ring_init(&ring->_ring, capacity)) #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_init(&ring->_mutex)) _Z_RETURN_IF_ERR(_z_condvar_init(&ring->_cv_not_empty)) #endif ring->is_closed = false; return _Z_RES_OK; } _z_ring_mt_t *_z_ring_mt_new(size_t capacity) { _z_ring_mt_t *ring = (_z_ring_mt_t *)z_malloc(sizeof(_z_ring_mt_t)); if (ring == NULL) { _Z_ERROR("z_malloc failed"); return NULL; } z_result_t ret = _z_ring_mt_init(ring, capacity); if (ret != _Z_RES_OK) { _Z_ERROR("_z_ring_mt_init failed: %i", ret); return NULL; } return ring; } void _z_ring_mt_clear(_z_ring_mt_t *ring, z_element_free_f free_f) { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&ring->_mutex); _z_condvar_drop(&ring->_cv_not_empty); #endif _z_ring_clear(&ring->_ring, free_f); } void _z_ring_mt_free(_z_ring_mt_t *ring, z_element_free_f free_f) { _z_ring_mt_clear(ring, free_f); z_free(ring); } z_result_t _z_ring_mt_push(const void *elem, void *context, z_element_free_f element_free) { if (elem == NULL || context == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_ring_mt_t *r = (_z_ring_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&r->_mutex)) #endif _z_ring_push_force_drop(&r->_ring, (void *)elem, element_free); #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_condvar_signal(&r->_cv_not_empty)) _Z_RETURN_IF_ERR(_z_mutex_unlock(&r->_mutex)) #endif return _Z_RES_OK; } z_result_t _z_ring_mt_close(_z_ring_mt_t *ring) { #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&ring->_mutex)) ring->is_closed = true; _Z_RETURN_IF_ERR(_z_condvar_signal_all(&ring->_cv_not_empty)) _Z_RETURN_IF_ERR(_z_mutex_unlock(&ring->_mutex)) #else ring->is_closed = true; #endif return _Z_RES_OK; } z_result_t _z_ring_mt_pull(void *dst, void *context, z_element_move_f element_move) { _z_ring_mt_t *r = (_z_ring_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 void *src = NULL; _Z_RETURN_IF_ERR(_z_mutex_lock(&r->_mutex)) while (src == NULL) { src = _z_ring_pull(&r->_ring); if (src == NULL) { if (r->is_closed) break; _Z_RETURN_IF_ERR(_z_condvar_wait(&r->_cv_not_empty, &r->_mutex)) } } _Z_RETURN_IF_ERR(_z_mutex_unlock(&r->_mutex)) #else // Z_FEATURE_MULTI_THREAD == 1 void *src = _z_ring_pull(&r->_ring); #endif // Z_FEATURE_MULTI_THREAD == 1 if (r->is_closed && src == NULL) { return _Z_RES_CHANNEL_CLOSED; } if (src != NULL) { element_move(dst, src); } return _Z_RES_OK; } z_result_t _z_ring_mt_try_pull(void *dst, void *context, z_element_move_f element_move) { _z_ring_mt_t *r = (_z_ring_mt_t *)context; #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&r->_mutex)) #endif void *src = _z_ring_pull(&r->_ring); #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_unlock(&r->_mutex)) #endif if (src != NULL) { element_move(dst, src); } else if (r->is_closed) { return _Z_RES_CHANNEL_CLOSED; } else { return _Z_RES_CHANNEL_NODATA; } return _Z_RES_OK; } ================================================ FILE: src/collections/slice.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/slice.h" #include #include #include #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" void _z_default_deleter(void *data, void *context) { _ZP_UNUSED(context); z_free(data); } void _z_static_deleter(void *data, void *context) { _ZP_UNUSED(data); _ZP_UNUSED(context); } _z_delete_context_t _z_delete_context_default(void) { return _z_delete_context_create(_z_default_deleter, NULL); } _z_delete_context_t _z_delete_context_static(void) { return _z_delete_context_create(_z_static_deleter, NULL); } /*-------- Slice --------*/ z_result_t _z_slice_init(_z_slice_t *bs, size_t capacity) { assert(bs != NULL); if (capacity == 0) { *bs = _z_slice_null(); return _Z_RES_OK; } bs->start = (uint8_t *)z_malloc(capacity); if (bs->start == NULL) { bs->len = 0; bs->_delete_context = _z_delete_context_null(); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } bs->len = capacity; bs->_delete_context = _z_delete_context_default(); return _Z_RES_OK; } _z_slice_t _z_slice_make(size_t capacity) { _z_slice_t bs; (void)_z_slice_init(&bs, capacity); return bs; } _z_slice_t _z_slice_copy_from_buf(const uint8_t *p, size_t len) { if (len == 0) { return _z_slice_null(); } _z_slice_t bs = _z_slice_alias_buf(p, len); return _z_slice_duplicate(&bs); } void _z_slice_free(_z_slice_t **bs) { _z_slice_t *ptr = *bs; if (ptr != NULL) { _z_slice_clear(ptr); z_free(ptr); *bs = NULL; } } z_result_t _z_slice_copy(_z_slice_t *dst, const _z_slice_t *src) { assert(src != NULL); assert(src->len == 0 || src->start != NULL); if (src->len == 0) { *dst = _z_slice_null(); return _Z_RES_OK; } // Make sure dst slice is not init beforehand, or suffer memory leak z_result_t ret = _z_slice_init(dst, src->len); if (ret == _Z_RES_OK) { (void)memcpy((uint8_t *)dst->start, src->start, src->len); } return ret; } z_result_t _z_slice_n_copy(_z_slice_t *dst, const _z_slice_t *src, size_t offset, size_t len) { assert(offset + len <= src->len); if (len == 0) { *dst = _z_slice_null(); return _Z_RES_OK; } // Make sure dst slice is not init beforehand, or suffer memory leak z_result_t ret = _z_slice_init(dst, len); if (ret == _Z_RES_OK) { const uint8_t *start = _z_cptr_u8_offset(src->start, (ptrdiff_t)offset); (void)memcpy((uint8_t *)dst->start, start, len); } return ret; } z_result_t _z_slice_move(_z_slice_t *dst, _z_slice_t *src) { // avoid moving of aliased slices if (!_z_slice_is_alloced(src)) { *dst = _z_slice_null(); _z_slice_t csrc; _Z_RETURN_IF_ERR(_z_slice_copy(&csrc, src)); *src = csrc; } *dst = *src; _z_slice_reset(src); return _Z_RES_OK; } _z_slice_t _z_slice_duplicate(const _z_slice_t *src) { _z_slice_t dst = _z_slice_null(); _z_slice_copy(&dst, src); return dst; } _z_slice_t _z_slice_steal(_z_slice_t *b) { _z_slice_t ret = *b; *b = _z_slice_null(); return ret; } bool _z_slice_eq(const _z_slice_t *left, const _z_slice_t *right) { assert(left != NULL); assert(right != NULL); if (left->len != right->len) { return false; } if (left->len == 0) { return true; } assert(left->start != NULL); assert(right->start != NULL); return memcmp(left->start, right->start, left->len) == 0; } bool _z_slice_is_alloced(const _z_slice_t *s) { return !_z_delete_context_is_null(&s->_delete_context); } ================================================ FILE: src/collections/sortedmap.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/sortedmap.h" #include #include #include #include "zenoh-pico/utils/logging.h" /*-------- sortedmap --------*/ void _z_sortedmap_init(_z_sortedmap_t *map, z_element_cmp_f f_cmp) { map->_vals = NULL; map->_f_cmp = f_cmp; } _z_sortedmap_t _z_sortedmap_make(z_element_cmp_f f_cmp) { _z_sortedmap_t map; _z_sortedmap_init(&map, f_cmp); return map; } size_t _z_sortedmap_len(const _z_sortedmap_t *map) { return _z_list_len(map->_vals); } bool _z_sortedmap_is_empty(const _z_sortedmap_t *map) { return _z_sortedmap_len(map) == (size_t)0; } z_result_t _z_sortedmap_copy(_z_sortedmap_t *dst, const _z_sortedmap_t *src, z_element_clone_f f_c) { assert((dst != NULL) && (src != NULL)); if (src->_vals != NULL) { dst->_vals = _z_list_clone(src->_vals, f_c); if (dst->_vals == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } dst->_f_cmp = src->_f_cmp; return _Z_RES_OK; } _z_sortedmap_t _z_sortedmap_clone(const _z_sortedmap_t *src, z_element_clone_f f_c, z_element_free_f f_f) { _z_sortedmap_t dst = {._vals = NULL, ._f_cmp = src->_f_cmp}; if (src->_vals == NULL) { return dst; } if (_z_sortedmap_copy(&dst, src, f_c) != _Z_RES_OK) { // Free the map _z_sortedmap_clear(&dst, f_f); } return dst; } void *_z_sortedmap_insert(_z_sortedmap_t *map, void *k, void *v, z_element_free_f f_f, bool replace) { if (map == NULL) { return NULL; } _z_list_t *prev = NULL; _z_list_t *curr = map->_vals; while (curr != NULL) { _z_sortedmap_entry_t *entry = (_z_sortedmap_entry_t *)_z_list_value(curr); int cmp = map->_f_cmp(k, entry->_key); if (cmp == 0) { if (!replace) { return NULL; } map->_vals = _z_list_drop_element(map->_vals, prev, f_f); break; } else if (cmp < 0) { break; // Found insertion point } prev = curr; curr = _z_list_next(curr); } _z_sortedmap_entry_t *entry = (_z_sortedmap_entry_t *)z_malloc(sizeof(_z_sortedmap_entry_t)); if (entry == NULL) { return NULL; } entry->_key = k; entry->_val = v; if (prev == NULL) { map->_vals = _z_list_push(map->_vals, entry); if (map->_vals == NULL) { z_free(entry); return NULL; } } else { _z_list_t *list = _z_list_push_after(prev, entry); if (list == NULL) { z_free(entry); return NULL; } } return v; } void *_z_sortedmap_get(const _z_sortedmap_t *map, const void *k) { void *ret = NULL; if (map->_vals != NULL) { _z_list_t *l = map->_vals; while (l != NULL) { // Check if the key matches _z_sortedmap_entry_t *entry = (_z_sortedmap_entry_t *)_z_list_value(l); if (map->_f_cmp(k, entry->_key) == 0) { ret = entry->_val; break; } l = _z_list_next(l); } } return ret; } _z_sortedmap_entry_t *_z_sortedmap_pop_first(_z_sortedmap_t *map) { _z_sortedmap_entry_t *ret = NULL; if (map->_vals != NULL) { _z_list_t *l = map->_vals; if (l != NULL) { ret = (_z_sortedmap_entry_t *)_z_list_value(l); map->_vals = _z_list_drop_element(map->_vals, NULL, _z_noop_free); } } return ret; } void _z_sortedmap_remove(_z_sortedmap_t *map, const void *k, z_element_free_f f) { if (map->_vals != NULL) { _z_list_t *prev = NULL; _z_list_t *curr = map->_vals; while (curr != NULL) { _z_sortedmap_entry_t *entry = (_z_sortedmap_entry_t *)_z_list_value(curr); if (map->_f_cmp(k, entry->_key) == 0) { map->_vals = _z_list_drop_element(map->_vals, prev, f); break; } prev = curr; curr = _z_list_next(curr); } } } _z_sortedmap_iterator_t _z_sortedmap_iterator_make(const _z_sortedmap_t *map) { _z_sortedmap_iterator_t iter = {0}; iter._map = map; iter._list_ptr = map->_vals; return iter; } bool _z_sortedmap_iterator_next(_z_sortedmap_iterator_t *iter) { if (!iter->_initialized) { iter->_list_ptr = iter->_map->_vals; iter->_initialized = true; } else if (iter->_list_ptr != NULL) { iter->_list_ptr = _z_list_next(iter->_list_ptr); } if (iter->_list_ptr != NULL) { iter->_entry = (_z_sortedmap_entry_t *)_z_list_value(iter->_list_ptr); return true; } return false; } void *_z_sortedmap_iterator_key(const _z_sortedmap_iterator_t *iter) { return iter->_entry->_key; } void *_z_sortedmap_iterator_value(const _z_sortedmap_iterator_t *iter) { return iter->_entry->_val; } void _z_sortedmap_clear(_z_sortedmap_t *map, z_element_free_f f_f) { if (map->_vals != NULL) { _z_list_free(&map->_vals, f_f); map->_vals = NULL; } } void _z_sortedmap_free(_z_sortedmap_t **map, z_element_free_f f) { _z_sortedmap_t *ptr = *map; if (ptr != NULL) { _z_sortedmap_clear(ptr, f); z_free(ptr); *map = NULL; } } ================================================ FILE: src/collections/string.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/string.h" #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" /*-------- string --------*/ _z_string_t _z_string_copy_from_str(const char *value) { _z_string_t s; s._slice = _z_slice_copy_from_buf((uint8_t *)value, strlen(value)); return s; } _z_string_t _z_string_copy_from_substr(const char *value, size_t len) { _z_string_t s; s._slice = _z_slice_copy_from_buf((uint8_t *)value, len); return s; } _z_string_t *_z_string_copy_from_str_as_ptr(const char *value) { _z_string_t *s = (_z_string_t *)z_malloc(sizeof(_z_string_t)); if (s == NULL) { return NULL; } *s = _z_string_copy_from_str(value); if (_z_slice_is_empty(&s->_slice) && value != NULL) { z_free(s); return NULL; } return s; } z_result_t _z_string_copy(_z_string_t *dst, const _z_string_t *src) { return _z_slice_copy(&dst->_slice, &src->_slice); } z_result_t _z_string_copy_substring(_z_string_t *dst, const _z_string_t *src, size_t offset, size_t len) { return _z_slice_n_copy(&dst->_slice, &src->_slice, offset, len); } z_result_t _z_string_move(_z_string_t *dst, _z_string_t *src) { return _z_slice_move(&dst->_slice, &src->_slice); } _z_string_t _z_string_steal(_z_string_t *str) { _z_string_t ret; ret._slice = _z_slice_steal(&str->_slice); return ret; } void _z_string_move_str(_z_string_t *dst, char *src) { *dst = _z_string_alias_str(src); } void _z_string_free(_z_string_t **str) { _z_string_t *ptr = *str; if (ptr != NULL) { _z_string_clear(ptr); z_free(ptr); *str = NULL; } } int _z_substring_compare(const _z_string_t *left, size_t left_start, size_t left_len, const _z_string_t *right, size_t right_start, size_t right_len) { int result = strncmp(_z_string_data(left) + left_start, _z_string_data(right) + right_start, left_len < right_len ? left_len : right_len); if (result == 0) { if (left_len < right_len) { return -1; } else if (left_len > right_len) { return 1; } } return result; } int _z_string_compare(const _z_string_t *left, const _z_string_t *right) { return _z_substring_compare(left, 0, _z_string_len(left), right, 0, _z_string_len(right)); } bool _z_string_equals(const _z_string_t *left, const _z_string_t *right) { if (_z_string_len(left) != _z_string_len(right)) { return false; } return (strncmp(_z_string_data(left), _z_string_data(right), _z_string_len(left)) == 0); } _z_string_t _z_string_convert_bytes_le(const _z_slice_t *bs) { _z_string_t s = _z_string_null(); size_t len = bs->len * (size_t)2; char *s_val = (char *)z_malloc((len) * sizeof(char)); if (s_val == NULL) { return s; } const char c[] = "0123456789abcdef"; size_t pos = bs->len * 2; for (size_t i = 0; i < bs->len; i++) { s_val[--pos] = c[bs->start[i] & (uint8_t)0x0F]; s_val[--pos] = c[(bs->start[i] & (uint8_t)0xF0) >> (uint8_t)4]; } s._slice = _z_slice_from_buf_custom_deleter((const uint8_t *)s_val, len, _z_delete_context_default()); return s; } _z_string_t _z_string_preallocate(size_t len) { _z_string_t s; // As long as _z_string_t is only a slice, no need to do anything more if (_z_slice_init(&s._slice, len) != _Z_RES_OK) { _Z_ERROR("String allocation failed"); } return s; } const char *_z_string_rchr(_z_string_t *str, char filter) { const char *curr_res = NULL; const char *ret = NULL; const char *curr_addr = _z_string_data(str); size_t curr_len = _z_string_len(str); do { curr_res = (char *)memchr(curr_addr, (int)filter, curr_len); if (curr_res != NULL) { ret = curr_res; curr_addr = curr_res + 1; curr_len = _z_ptr_char_diff(curr_addr, _z_string_data(str)); if (curr_len >= _z_string_len(str)) { break; } curr_len = _z_string_len(str) - curr_len; } } while (curr_res != NULL); return ret; } char *_z_string_pbrk(_z_string_t *str, const char *filter) { const char *data = _z_string_data(str); for (size_t idx = 0; idx < _z_string_len(str); idx++) { const char *curr_char = filter; while (*curr_char != '\0') { if (data[idx] == *curr_char) { return (char *)&data[idx]; } curr_char++; } } return NULL; } /*-------- str --------*/ size_t _z_str_size(const char *src) { return strlen(src) + (size_t)1; } void _z_str_clear(char *src) { z_free(src); } void _z_str_free(char **src) { char *ptr = *src; if (ptr != NULL) { _z_str_clear(ptr); *src = NULL; } } void _z_str_copy(char *dst, const char *src) { size_t size = _z_str_size(src); strncpy(dst, src, size - 1); dst[size - 1] = '\0'; // No matter what, strings are always null-terminated upon copy } void _z_str_n_copy(char *dst, const char *src, size_t size) { strncpy(dst, src, size - 1); dst[size - 1] = '\0'; // No matter what, strings are always null-terminated upon copy } char *_z_str_clone(const char *src) { size_t len = _z_str_size(src); char *dst = (char *)z_malloc(len); if (dst != NULL) { _z_str_n_copy(dst, src, len); } return dst; } char *_z_str_n_clone(const char *src, size_t len) { char *dst = (char *)z_malloc(len + 1); if (dst != NULL) { _z_str_n_copy(dst, src, len + 1); } return dst; } char *_z_str_from_string_clone(const _z_string_t *str) { return _z_str_n_clone((const char *)str->_slice.start, str->_slice.len); } bool _z_str_eq(const char *left, const char *right) { return strcmp(left, right) == 0; } int _z_str_cmp(const char *left, const char *right) { return strcmp(left, right); } z_result_t _z_string_concat_substr(_z_string_t *s, const _z_string_t *left, const char *right, size_t len, const char *separator, size_t separator_len) { if (separator == NULL && separator_len != 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } *s = _z_string_null(); size_t left_len = _z_string_len(left); if (len == 0) { return _z_string_copy(s, left); } else if (right == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } else if (left_len == 0) { *s = _z_string_copy_from_substr(right, len); if (!_z_string_check(s)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } } *s = _z_string_preallocate(left_len + len + separator_len); if (!_z_string_check(s)) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } uint8_t *curr_ptr = (uint8_t *)_z_string_data(s); // SAFETY: _z_string_preallocate and check of its result above ensures bound checks. // Flawfinder: ignore [CWE-120] memcpy(curr_ptr, _z_string_data(left), left_len); curr_ptr += left_len; if (separator_len > 0) { // SAFETY: _z_string_preallocate and check of its result above ensures bound checks. // Flawfinder: ignore [CWE-120] memcpy(curr_ptr, separator, separator_len); curr_ptr += separator_len; } // SAFETY: _z_string_preallocate and check of its result above ensures bound checks. // Flawfinder: ignore [CWE-120] memcpy(curr_ptr, right, len); return _Z_RES_OK; } ================================================ FILE: src/collections/sync_group.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/sync_group.h" #include "assert.h" static inline bool __unsafe_z_sync_group_has_no_alive_notifiers(const _z_sync_group_t* sync_group) { // NOTE: the function is unsafe because it requires sync_group_state mutex to be locked return _z_sync_group_state_rc_weak_count(&sync_group->_state) == 0; } z_result_t _z_sync_group_state_create(_z_sync_group_state_t* state) { state->is_closed = true; #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_init(&state->counter_mutex)); _Z_CLEAN_RETURN_IF_ERR(_z_condvar_init(&state->counter_condvar), _z_mutex_drop(&state->counter_mutex)); #endif state->is_closed = false; return Z_OK; } void _z_sync_group_state_clear(_z_sync_group_state_t* state) { if (state != NULL) { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&state->counter_mutex); _z_condvar_drop(&state->counter_condvar); #endif } } z_result_t _z_sync_group_wait(_z_sync_group_t* sync_group) { if (_Z_RC_IS_NULL(&sync_group->_state)) { return _Z_ERR_NULL; } _z_sync_group_state_t* state = _Z_RC_IN_VAL(&sync_group->_state); #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&state->counter_mutex)); while (!state->is_closed && !__unsafe_z_sync_group_has_no_alive_notifiers(sync_group)) { _Z_RETURN_IF_ERR(_z_condvar_wait(&state->counter_condvar, &state->counter_mutex)); } z_result_t ret = _Z_RES_OK; if (state->is_closed) { ret = Z_SYNC_GROUP_CLOSED; } else { state->is_closed = true; } _z_mutex_unlock(&state->counter_mutex); return ret; #else if (state->is_closed) { return Z_SYNC_GROUP_CLOSED; } else if (__unsafe_z_sync_group_has_no_alive_notifiers(sync_group)) { state->is_closed = true; return _Z_RES_OK; } else { return _Z_ERR_GENERIC; } #endif } void _z_sync_group_close(_z_sync_group_t* sync_group) { if (_Z_RC_IS_NULL(&sync_group->_state)) { return; } _z_sync_group_state_t* state = _Z_RC_IN_VAL(&sync_group->_state); #if Z_FEATURE_MULTI_THREAD == 1 if (_z_mutex_lock(&state->counter_mutex) != _Z_RES_OK) { return; } #endif state->is_closed = true; #if Z_FEATURE_MULTI_THREAD == 1 _z_condvar_signal_all(&state->counter_condvar); _z_mutex_unlock(&state->counter_mutex); #endif } z_result_t _z_sync_group_wait_deadline(_z_sync_group_t* sync_group, const z_clock_t* deadline) { if (_Z_RC_IS_NULL(&sync_group->_state)) { return _Z_ERR_NULL; } _z_sync_group_state_t* state = _Z_RC_IN_VAL(&sync_group->_state); #if Z_FEATURE_MULTI_THREAD == 1 z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_mutex_lock(&state->counter_mutex)); while (!state->is_closed && !__unsafe_z_sync_group_has_no_alive_notifiers(sync_group)) { ret = _z_condvar_wait_until(&state->counter_condvar, &state->counter_mutex, deadline); if (ret == Z_ETIMEDOUT) { break; } else if (ret != _Z_RES_OK) { return ret; } } if (state->is_closed) { ret = Z_SYNC_GROUP_CLOSED; } else if (ret == _Z_RES_OK) { state->is_closed = true; } _z_mutex_unlock(&state->counter_mutex); return ret; #else _ZP_UNUSED(deadline); if (state->is_closed) { return Z_SYNC_GROUP_CLOSED; } else if (__unsafe_z_sync_group_has_no_alive_notifiers(sync_group)) { state->is_closed = true; return _Z_RES_OK; } else { return Z_ETIMEDOUT; } #endif } z_result_t _z_sync_group_create(_z_sync_group_t* sync_group) { _z_sync_group_state_t* state = (_z_sync_group_state_t*)z_malloc(sizeof(_z_sync_group_state_t)); if (state == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _Z_CLEAN_RETURN_IF_ERR(_z_sync_group_state_create(state), z_free(state)); sync_group->_state = _z_sync_group_state_rc_new(state); if (_Z_RC_IS_NULL(&sync_group->_state)) { _z_sync_group_state_clear(state); z_free(state); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return _Z_RES_OK; } void _z_sync_group_drop(_z_sync_group_t* sync_group) { _z_sync_group_state_rc_drop(&sync_group->_state); } z_result_t _z_sync_group_create_notifier(const _z_sync_group_t* sync_group, _z_sync_group_notifier_t* notifier) { if (_Z_RC_IS_NULL(&sync_group->_state)) { return _Z_ERR_NULL; } *notifier = _z_sync_group_notifier_null(); #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_lock(&_Z_RC_IN_VAL(&sync_group->_state)->counter_mutex)); #endif if (_Z_RC_IN_VAL(&sync_group->_state)->is_closed) { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&_Z_RC_IN_VAL(&sync_group->_state)->counter_mutex); #endif return Z_SYNC_GROUP_CLOSED; } notifier->_state = _z_sync_group_state_rc_clone_as_weak(&sync_group->_state); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&_Z_RC_IN_VAL(&sync_group->_state)->counter_mutex); #endif return _Z_RC_IS_NULL(¬ifier->_state) ? _Z_ERR_SYSTEM_OUT_OF_MEMORY : _Z_RES_OK; } void _z_sync_group_notifier_drop(_z_sync_group_notifier_t* notifier) { if (_Z_RC_IS_NULL(¬ifier->_state)) { return; } _z_sync_group_state_rc_t state_rc = _z_sync_group_state_weak_upgrade(¬ifier->_state); if (!_Z_RC_IS_NULL(&state_rc)) { #if Z_FEATURE_MULTI_THREAD == 1 if (_z_mutex_lock(&_Z_RC_IN_VAL(&state_rc)->counter_mutex) != _Z_RES_OK) { _z_sync_group_state_weak_drop(¬ifier->_state); _z_sync_group_state_rc_drop(&state_rc); return; } #endif _z_sync_group_state_weak_drop(¬ifier->_state); #if Z_FEATURE_MULTI_THREAD == 1 _z_condvar_signal_all(&_Z_RC_IN_VAL(&state_rc)->counter_condvar); _z_mutex_unlock(&_Z_RC_IN_VAL(&state_rc)->counter_mutex); #endif _z_sync_group_state_rc_drop(&state_rc); } else { _z_sync_group_state_weak_drop(¬ifier->_state); } } bool _z_sync_group_is_closed(const _z_sync_group_t* sync_group) { #if Z_FEATURE_MULTI_THREAD == 1 if (_z_mutex_lock(&_Z_RC_IN_VAL(&sync_group->_state)->counter_mutex) != _Z_RES_OK) { return true; } #endif bool ret = _Z_RC_IN_VAL(&sync_group->_state)->is_closed; #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_unlock(&_Z_RC_IN_VAL(&sync_group->_state)->counter_mutex); #endif return ret; } ================================================ FILE: src/collections/vec.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/collections/vec.h" #include #include #include #include "zenoh-pico/utils/logging.h" /*-------- vec --------*/ _z_vec_t _z_vec_make(size_t capacity) { _z_vec_t v = {0}; if (capacity != 0) { v._val = (void **)z_malloc(sizeof(void *) * capacity); if (v._val != NULL) { v._capacity = capacity; } } return v; } void _z_vec_copy(_z_vec_t *dst, const _z_vec_t *src, z_element_clone_f d_f) { dst->_capacity = src->_capacity; dst->_len = src->_len; dst->_val = (void **)z_malloc(sizeof(void *) * src->_capacity); if (dst->_val != NULL) { for (size_t i = 0; i < src->_len; i++) { dst->_val[i] = d_f(src->_val[i]); } } } void _z_vec_move(_z_vec_t *dst, _z_vec_t *src) { *dst = *src; *src = _z_vec_null(); } void _z_vec_reset(_z_vec_t *v, z_element_free_f free_f) { for (size_t i = 0; i < v->_len; i++) { free_f(&v->_val[i]); } v->_len = 0; } void _z_vec_clear(_z_vec_t *v, z_element_free_f free_f) { for (size_t i = 0; i < v->_len; i++) { free_f(&v->_val[i]); } _z_vec_release(v); } void _z_vec_release(_z_vec_t *v) { z_free(v->_val); v->_val = NULL; v->_capacity = 0; v->_len = 0; } void _z_vec_free(_z_vec_t **v, z_element_free_f free_f) { _z_vec_t *ptr = (_z_vec_t *)*v; if (ptr != NULL) { _z_vec_clear(ptr, free_f); z_free(ptr); *v = NULL; } } void _z_vec_append(_z_vec_t *v, void *e) { if (v->_len == v->_capacity) { // Allocate a new vector size_t _capacity = (v->_capacity << 1) | 0x01; void **_val = (void **)z_malloc(_capacity * sizeof(void *)); if (_val != NULL) { (void)memcpy(_val, v->_val, v->_capacity * sizeof(void *)); // Free the old vector z_free(v->_val); // Update the current vector v->_val = _val; v->_capacity = _capacity; v->_val[v->_len] = e; v->_len = v->_len + 1; } } else { v->_val[v->_len] = e; v->_len = v->_len + 1; } } void _z_vec_set(_z_vec_t *v, size_t i, void *e, z_element_free_f free_f) { assert(i < v->_len); if (v->_val[i] != NULL) { free_f(&v->_val[i]); } v->_val[i] = e; } void _z_vec_remove(_z_vec_t *v, size_t pos, z_element_free_f free_f) { free_f(&v->_val[pos]); for (size_t i = pos; i < v->_len; i++) { v->_val[pos] = v->_val[pos + (size_t)1]; } v->_val[v->_len] = NULL; v->_len = v->_len - 1; } /*-------- svec --------*/ _z_svec_t _z_svec_make(size_t capacity, size_t element_size) { _z_svec_t v = _z_svec_null(); if (capacity != 0) { v._val = z_malloc(element_size * capacity); } if (v._val != NULL) { v._capacity = capacity; } return v; } void _z_svec_init(_z_svec_t *v, size_t offset, size_t element_size) { assert(offset <= v->_capacity); void *start = _z_svec_get_mut(v, offset, element_size); memset(start, 0, (v->_capacity - offset) * element_size); } static inline void __z_svec_move_inner(void *dst, void *src, z_element_move_f move, size_t num_elements, size_t element_size, bool use_elem_f) { if (use_elem_f) { size_t offset = 0; for (size_t i = 0; i < num_elements; i++) { move((uint8_t *)dst + offset, (uint8_t *)src + offset); offset += element_size; } } else { memmove(dst, src, num_elements * element_size); } } void _z_svec_move(_z_svec_t *dst, _z_svec_t *src) { *dst = *src; *src = _z_svec_null(); } z_result_t _z_svec_copy(_z_svec_t *dst, const _z_svec_t *src, z_element_copy_f copy, size_t element_size, bool use_elem_f) { *dst = _z_svec_null(); if (src->_capacity == 0) { return _Z_RES_OK; } dst->_val = z_malloc(element_size * src->_capacity); if (dst->_val == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } dst->_capacity = src->_capacity; dst->_len = src->_len; // Copy data to new vector if (use_elem_f) { size_t offset = 0; for (size_t i = 0; i < src->_len; i++) { copy((uint8_t *)dst->_val + offset, (uint8_t *)src->_val + offset); offset += element_size; } } else { memcpy(dst->_val, src->_val, src->_len * element_size); } return _Z_RES_OK; } void _z_svec_reset(_z_svec_t *v, z_element_clear_f clear, size_t element_size) { if (clear != NULL) { size_t offset = 0; for (size_t i = 0; i < v->_len; i++) { clear((uint8_t *)v->_val + offset); offset += element_size; } } v->_len = 0; } void _z_svec_clear(_z_svec_t *v, z_element_clear_f clear_f, size_t element_size) { _z_svec_reset(v, clear_f, element_size); if (!v->_aliased) { _z_svec_release(v); } } void _z_svec_release(_z_svec_t *v) { z_free(v->_val); v->_capacity = 0; v->_val = NULL; } void _z_svec_free(_z_svec_t **v, z_element_clear_f clear, size_t element_size) { _z_svec_t *ptr = (_z_svec_t *)*v; if (ptr != NULL) { _z_svec_clear(ptr, clear, element_size); z_free(ptr); *v = NULL; } } z_result_t _z_svec_expand(_z_svec_t *v, z_element_move_f move, size_t element_size, bool use_elem_f) { // Allocate a new vector size_t _capacity = v->_capacity == 0 ? 1 : v->_capacity << 1; void *_val = (void *)z_malloc(_capacity * element_size); if (_val == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Move and clear old data __z_svec_move_inner(_val, v->_val, move, v->_len, element_size, use_elem_f); z_free(v->_val); // Update the current vector v->_val = _val; v->_capacity = _capacity; return _Z_RES_OK; } z_result_t _z_svec_append(_z_svec_t *v, const void *e, z_element_move_f move, size_t element_size, bool use_elem_f) { if (v->_len == v->_capacity) { _Z_RETURN_IF_ERR(_z_svec_expand(v, move, element_size, use_elem_f)); } // Append element memcpy((uint8_t *)v->_val + v->_len * element_size, e, element_size); v->_len++; return _Z_RES_OK; } void _z_svec_set(_z_svec_t *v, size_t i, void *e, z_element_clear_f clear, size_t element_size) { assert(i < v->_len); clear((uint8_t *)v->_val + i * element_size); memcpy((uint8_t *)v->_val + i * element_size, e, element_size); } void _z_svec_remove(_z_svec_t *v, size_t pos, z_element_clear_f clear, z_element_move_f move, size_t element_size, bool use_elem_f) { assert(pos < v->_len); clear((uint8_t *)v->_val + pos * element_size); __z_svec_move_inner((uint8_t *)v->_val + pos * element_size, (uint8_t *)v->_val + (pos + 1) * element_size, move, (v->_len - pos - 1) * element_size, element_size, use_elem_f); v->_len--; } ================================================ FILE: src/link/config/bt.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/link/config/bt.h" #include #if Z_FEATURE_LINK_BLUETOOTH == 1 size_t _z_bt_config_strlen(const _z_str_intmap_t *s) { BT_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, BT_CONFIG_ARGC, args); } void _z_bt_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { BT_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, BT_CONFIG_ARGC, args); } char *_z_bt_config_to_str(const _z_str_intmap_t *s) { BT_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, BT_CONFIG_ARGC, args); } z_result_t _z_bt_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { BT_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, BT_CONFIG_ARGC, args, n); } z_result_t _z_bt_config_from_str(_z_str_intmap_t *strint, const char *s) { BT_CONFIG_MAPPING_BUILD return _z_str_intmap_from_str(strint, s, BT_CONFIG_ARGC, args); } #endif ================================================ FILE: src/link/config/serial.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/serial.h" #include #if Z_FEATURE_LINK_SERIAL == 1 size_t _z_serial_config_strlen(const _z_str_intmap_t *s) { SERIAL_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, SERIAL_CONFIG_ARGC, args); } void _z_serial_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { SERIAL_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, SERIAL_CONFIG_ARGC, args); } char *_z_serial_config_to_str(const _z_str_intmap_t *s) { SERIAL_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, SERIAL_CONFIG_ARGC, args); } z_result_t _z_serial_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { SERIAL_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, SERIAL_CONFIG_ARGC, args, n); } z_result_t _z_serial_config_from_str(_z_str_intmap_t *strint, const char *s) { SERIAL_CONFIG_MAPPING_BUILD return _z_str_intmap_from_str(strint, s, SERIAL_CONFIG_ARGC, args); } #endif ================================================ FILE: src/link/config/tcp.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/tcp.h" #include #include "zenoh-pico/config.h" #if Z_FEATURE_LINK_TCP == 1 size_t _z_tcp_config_strlen(const _z_str_intmap_t *s) { TCP_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, TCP_CONFIG_ARGC, args); } void _z_tcp_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { TCP_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, TCP_CONFIG_ARGC, args); } char *_z_tcp_config_to_str(const _z_str_intmap_t *s) { TCP_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, TCP_CONFIG_ARGC, args); } z_result_t _z_tcp_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { TCP_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, TCP_CONFIG_ARGC, args, n); } z_result_t _z_tcp_config_from_str(_z_str_intmap_t *strint, const char *s) { return _z_tcp_config_from_strn(strint, s, strlen(s)); } #endif ================================================ FILE: src/link/config/tls.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/tls.h" #include #if Z_FEATURE_LINK_TLS == 1 size_t _z_tls_config_strlen(const _z_str_intmap_t *s) { TLS_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, TLS_CONFIG_ARGC, args); } void _z_tls_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { TLS_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, TLS_CONFIG_ARGC, args); } char *_z_tls_config_to_str(const _z_str_intmap_t *s) { TLS_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, TLS_CONFIG_ARGC, args); } z_result_t _z_tls_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { TLS_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, TLS_CONFIG_ARGC, args, n); } z_result_t _z_tls_config_from_str(_z_str_intmap_t *strint, const char *s) { TLS_CONFIG_MAPPING_BUILD return _z_str_intmap_from_str(strint, s, TLS_CONFIG_ARGC, args); } #endif ================================================ FILE: src/link/config/udp.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/udp.h" #include #include "zenoh-pico/config.h" #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 size_t _z_udp_config_strlen(const _z_str_intmap_t *s) { UDP_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, UDP_CONFIG_ARGC, args); } void _z_udp_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { UDP_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, UDP_CONFIG_ARGC, args); } char *_z_udp_config_to_str(const _z_str_intmap_t *s) { UDP_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, UDP_CONFIG_ARGC, args); } z_result_t _z_udp_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { UDP_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, UDP_CONFIG_ARGC, args, n); } z_result_t _z_udp_config_from_str(_z_str_intmap_t *strint, const char *s) { return _z_udp_config_from_strn(strint, s, strlen(s)); } #endif ================================================ FILE: src/link/config/ws.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/ws.h" #include #include "zenoh-pico/config.h" #if Z_FEATURE_LINK_WS == 1 size_t _z_ws_config_strlen(const _z_str_intmap_t *s) { WS_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, argc, args); } void _z_ws_config_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { WS_CONFIG_MAPPING_BUILD _z_str_intmap_onto_str(dst, dst_len, s, argc, args); } char *_z_ws_config_to_str(const _z_str_intmap_t *s) { WS_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, argc, args); } z_result_t _z_ws_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { WS_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, argc, args, n); } z_result_t _z_ws_config_from_str(_z_str_intmap_t *strint, const char *s) { return _z_ws_config_from_strn(strint, s, strlen(s)); } #endif ================================================ FILE: src/link/endpoint.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/endpoint.h" #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #if Z_FEATURE_LINK_TCP == 1 #include "zenoh-pico/link/config/tcp.h" #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 #include "zenoh-pico/link/config/udp.h" #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #include "zenoh-pico/link/config/bt.h" #endif #if Z_FEATURE_LINK_SERIAL == 1 #include "zenoh-pico/link/config/serial.h" #endif #if Z_FEATURE_LINK_WS == 1 #include "zenoh-pico/link/config/ws.h" #endif #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/config/tls.h" #endif #include "zenoh-pico/link/config/raweth.h" /*------------------ Locator ------------------*/ void _z_locator_init(_z_locator_t *locator) { locator->_protocol = _z_string_null(); locator->_address = _z_string_null(); locator->_metadata = _z_str_intmap_make(); } void _z_locator_clear(_z_locator_t *lc) { _z_string_clear(&lc->_protocol); _z_string_clear(&lc->_address); _z_str_intmap_clear(&lc->_metadata); } void _z_locator_free(_z_locator_t **lc) { _z_locator_t *ptr = *lc; if (ptr != NULL) { _z_locator_clear(ptr); z_free(ptr); *lc = NULL; } } z_result_t _z_locator_copy(_z_locator_t *dst, const _z_locator_t *src) { _Z_RETURN_IF_ERR(_z_string_copy(&dst->_protocol, &src->_protocol)); _Z_RETURN_IF_ERR(_z_string_copy(&dst->_address, &src->_address)); // @TODO: implement copy for metadata dst->_metadata = _z_str_intmap_make(); return _Z_RES_OK; } bool _z_locator_eq(const _z_locator_t *left, const _z_locator_t *right) { bool res = false; res = _z_string_equals(&left->_protocol, &right->_protocol); if (res == true) { res = _z_string_equals(&left->_address, &right->_address); // if (res == true) { // // @TODO: implement eq for metadata // } } return res; } static z_result_t _z_locator_protocol_from_string(_z_string_t *protocol, const _z_string_t *str) { *protocol = _z_string_null(); const char *p_start = _z_string_data(str); const char *p_end = (char *)memchr(p_start, (int)LOCATOR_PROTOCOL_SEPARATOR, _z_string_len(str)); if ((p_end == NULL) || (p_start == p_end)) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } size_t p_len = _z_ptr_char_diff(p_end, p_start); return _z_string_copy_substring(protocol, str, 0, p_len); } static z_result_t _z_locator_address_from_string(_z_string_t *address, const _z_string_t *str) { *address = _z_string_null(); // Find protocol separator const char *p_start = (char *)memchr(_z_string_data(str), (int)LOCATOR_PROTOCOL_SEPARATOR, _z_string_len(str)); if (p_start == NULL) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } // Skip protocol separator p_start = _z_cptr_char_offset(p_start, 1); size_t start_offset = _z_ptr_char_diff(p_start, _z_string_data(str)); if (start_offset >= _z_string_len(str)) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } // Find metadata separator size_t curr_len = _z_string_len(str) - start_offset; const char *p_end = (char *)memchr(p_start, (int)LOCATOR_METADATA_SEPARATOR, curr_len); // There is no metadata separator, then look for config separator if (p_end == NULL) { p_end = memchr(p_start, (int)ENDPOINT_CONFIG_SEPARATOR, curr_len); } // There is no config separator, then address goes to the end of string if (p_end == NULL) { p_end = _z_cptr_char_offset(_z_string_data(str), (ptrdiff_t)_z_string_len(str)); } if (p_start >= p_end) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } // Copy data size_t addr_len = _z_ptr_char_diff(p_end, p_start); return _z_string_copy_substring(address, str, start_offset, addr_len); } z_result_t _z_locator_metadata_from_string(_z_str_intmap_t *strint, const _z_string_t *str) { *strint = _z_str_intmap_make(); // Find metadata separator const char *p_start = (char *)memchr(_z_string_data(str), LOCATOR_METADATA_SEPARATOR, _z_string_len(str)); if (p_start == NULL) { return _Z_RES_OK; } p_start = _z_cptr_char_offset(p_start, 1); size_t start_offset = _z_ptr_char_diff(p_start, _z_string_data(str)); if (start_offset > _z_string_len(str)) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } if (start_offset == _z_string_len(str)) { return _Z_RES_OK; } const char *p_end = (char *)memchr(_z_string_data(str), ENDPOINT_CONFIG_SEPARATOR, _z_string_len(str)); if (p_end == NULL) { p_end = _z_cptr_char_offset(_z_string_data(str), (ptrdiff_t)_z_string_len(str) + 1); } if (p_start != p_end) { size_t p_len = _z_ptr_char_diff(p_end, p_start); return _z_str_intmap_from_strn(strint, p_start, 0, NULL, p_len); } return _Z_RES_OK; } size_t _z_locator_metadata_strlen(const _z_str_intmap_t *s) { // @TODO: define protocol-level metadata return _z_str_intmap_strlen(s, 0, NULL); } void _z_locator_metadata_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s) { // @TODO: define protocol-level metadata _z_str_intmap_onto_str(dst, dst_len, s, 0, NULL); } z_result_t _z_locator_from_string(_z_locator_t *lc, const _z_string_t *str) { if (str == NULL || !_z_string_check(str)) { _Z_ERROR_RETURN(_Z_ERR_CONFIG_LOCATOR_INVALID); } // Parse protocol _Z_RETURN_IF_ERR(_z_locator_protocol_from_string(&lc->_protocol, str)); // Parse address _Z_CLEAN_RETURN_IF_ERR(_z_locator_address_from_string(&lc->_address, str), _z_locator_clear(lc)); // Parse metadata _Z_CLEAN_RETURN_IF_ERR(_z_locator_metadata_from_string(&lc->_metadata, str), _z_locator_clear(lc)); return _Z_RES_OK; } size_t _z_locator_strlen(const _z_locator_t *l) { size_t ret = 0; if (l != NULL) { // Calculate the string length to allocate ret = _z_string_len(&l->_protocol) + _z_string_len(&l->_address) + 1; // @TODO: define protocol-level metadata size_t md_len = _z_locator_metadata_strlen(&l->_metadata); if (md_len > (size_t)0) { ret = ret + (size_t)1; // Locator metadata separator ret = ret + md_len; // Locator metadata content } } return ret; } /** * Converts a :c:type:`_z_locator_t` into its string format. * * Parameters: * dst: Pointer to the destination string. It MUST be already allocated with enough space to store the locator in * its string format. loc: :c:type:`_z_locator_t` to be converted into its string format. */ static void __z_locator_onto_string(_z_string_t *dst, const _z_locator_t *loc) { char *curr_dst = (char *)_z_string_data(dst); const char psep = LOCATOR_PROTOCOL_SEPARATOR; const char msep = LOCATOR_METADATA_SEPARATOR; size_t prot_len = _z_string_len(&loc->_protocol); size_t addr_len = _z_string_len(&loc->_address); if ((prot_len + addr_len + 1) > _z_string_len(dst)) { _Z_ERROR("Buffer too small to write locator"); return; } // Locator protocol memcpy(curr_dst, _z_string_data(&loc->_protocol), prot_len); curr_dst = _z_ptr_char_offset(curr_dst, (ptrdiff_t)prot_len); // Locator protocol separator memcpy(curr_dst, &psep, 1); curr_dst = _z_ptr_char_offset(curr_dst, 1); // Locator address memcpy(curr_dst, _z_string_data(&loc->_address), addr_len); curr_dst = _z_ptr_char_offset(curr_dst, (ptrdiff_t)addr_len); // @TODO: define protocol-level metadata size_t md_len = _z_locator_metadata_strlen(&loc->_metadata); if (md_len > (size_t)0) { size_t curr_len = _z_string_len(dst) - _z_ptr_char_diff(curr_dst, _z_string_data(dst)); if (curr_len == 0) { _Z_ERROR("Buffer too small to write metadata"); return; } // Locator metadata separator memcpy(curr_dst, &msep, 1); curr_dst = _z_ptr_char_offset(curr_dst, 1); // Locator metadata _z_locator_metadata_onto_str(curr_dst, curr_len, &loc->_metadata); } } /** * Converts a :c:type:`_z_locator_t` into its _z_string format. * * Parameters: * loc: :c:type:`_z_locator_t` to be converted into its _z_string format. * * Returns: * The z_stringified :c:type:`_z_locator_t`. */ _z_string_t _z_locator_to_string(const _z_locator_t *loc) { _z_string_t s = _z_string_preallocate(_z_locator_strlen(loc)); if (!_z_string_check(&s)) { return s; } __z_locator_onto_string(&s, loc); return s; } static const char *_z_endpoint_rchr(const _z_string_t *addr, char filter) { const char *addr_data = _z_string_data(addr); size_t addr_len = _z_string_len(addr); while (addr_len > 0) { addr_len--; if (addr_data[addr_len] == filter) { return &addr_data[addr_len]; } } return NULL; } char *_z_endpoint_parse_host(const _z_string_t *addr) { if (addr == NULL) { return NULL; } const char *addr_data = _z_string_data(addr); const size_t addr_len = _z_string_len(addr); if (addr_data == NULL || addr_len == 0) { return NULL; } const char *colon = _z_endpoint_rchr(addr, ':'); if (colon == NULL) { return NULL; } // IPv6 const char *host_start = addr_data; const char *host_end = colon; if ((host_end > host_start) && (host_start[0] == '[') && (host_end[-1] == ']')) { host_start = _z_cptr_char_offset(host_start, 1); host_end = _z_cptr_char_offset(host_end, -1); } if (host_end <= host_start) { return NULL; } const size_t host_len = _z_ptr_char_diff(host_end, host_start); char *host_copy = (char *)z_malloc(host_len + 1); if (host_copy == NULL) { return NULL; } _z_str_n_copy(host_copy, host_start, host_len + 1); return host_copy; } char *_z_endpoint_parse_port(const _z_string_t *addr) { if (addr == NULL) { return NULL; } const char *addr_data = _z_string_data(addr); const size_t addr_len = _z_string_len(addr); if (addr_data == NULL || addr_len == 0) { return NULL; } const char *colon = _z_endpoint_rchr(addr, ':'); if (colon == NULL) { return NULL; } const char *addr_end = _z_cptr_char_offset(addr_data, (ptrdiff_t)addr_len); const char *port_start = _z_cptr_char_offset(colon, 1); if (port_start >= addr_end) { return NULL; } const size_t port_len = _z_ptr_char_diff(addr_end, port_start); char *port = (char *)z_malloc(port_len + 1); if (port == NULL) { return NULL; } _z_str_n_copy(port, port_start, port_len + 1); char *endptr = NULL; errno = 0; unsigned long port_val = strtoul(port, &endptr, 10); if ((errno != 0) || (endptr == port) || (*endptr != '\0') || (port_val == 0) || (port_val > 65535)) { z_free(port); return NULL; } return port; } /*------------------ Endpoint ------------------*/ void _z_endpoint_init(_z_endpoint_t *endpoint) { _z_locator_init(&endpoint->_locator); endpoint->_config = _z_str_intmap_make(); } void _z_endpoint_clear(_z_endpoint_t *ep) { _z_locator_clear(&ep->_locator); _z_str_intmap_clear(&ep->_config); } void _z_endpoint_free(_z_endpoint_t **ep) { _z_endpoint_t *ptr = *ep; if (ptr != NULL) { _z_locator_clear(&ptr->_locator); _z_str_intmap_clear(&ptr->_config); z_free(ptr); *ep = NULL; } } z_result_t _z_endpoint_config_from_string(_z_str_intmap_t *strint, const _z_string_t *str, _z_string_t *proto) { char *p_start = (char *)memchr(_z_string_data(str), ENDPOINT_CONFIG_SEPARATOR, _z_string_len(str)); if (p_start != NULL) { p_start = _z_ptr_char_offset(p_start, 1); size_t cfg_size = _z_string_len(str) - _z_ptr_char_diff(p_start, _z_string_data(str)); // Call the right configuration parser depending on the protocol _z_string_t cmp_str = _z_string_null(); #if Z_FEATURE_LINK_TCP == 1 cmp_str = _z_string_alias_str(TCP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tcp_config_from_strn(strint, p_start, cfg_size); } #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 cmp_str = _z_string_alias_str(UDP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_udp_config_from_strn(strint, p_start, cfg_size); } #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 cmp_str = _z_string_alias_str(BT_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_bt_config_from_strn(strint, p_start, cfg_size); } #endif #if Z_FEATURE_LINK_SERIAL == 1 cmp_str = _z_string_alias_str(SERIAL_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_serial_config_from_strn(strint, p_start, cfg_size); } #endif #if Z_FEATURE_LINK_WS == 1 cmp_str = _z_string_alias_str(WS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_ws_config_from_strn(strint, p_start, cfg_size); } #endif #if Z_FEATURE_LINK_TLS == 1 cmp_str = _z_string_alias_str(TLS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tls_config_from_strn(strint, p_start, cfg_size); } #endif cmp_str = _z_string_alias_str(RAWETH_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_raweth_config_from_strn(strint, p_start, cfg_size); } } return _Z_RES_OK; } size_t _z_endpoint_config_strlen(const _z_str_intmap_t *s, _z_string_t *proto) { // Call the right configuration parser depending on the protocol _z_string_t cmp_str = _z_string_null(); #if Z_FEATURE_LINK_TCP == 1 cmp_str = _z_string_alias_str(TCP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tcp_config_strlen(s); } #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 cmp_str = _z_string_alias_str(UDP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_udp_config_strlen(s); } #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 cmp_str = _z_string_alias_str(BT_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_bt_config_strlen(s); } #endif #if Z_FEATURE_LINK_SERIAL == 1 cmp_str = _z_string_alias_str(SERIAL_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_serial_config_strlen(s); } #endif #if Z_FEATURE_LINK_WS == 1 cmp_str = _z_string_alias_str(WS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_ws_config_strlen(s); } #endif #if Z_FEATURE_LINK_TLS == 1 cmp_str = _z_string_alias_str(TLS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tls_config_strlen(s); } #endif cmp_str = _z_string_alias_str(RAWETH_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_raweth_config_strlen(s); } return 0; } char *_z_endpoint_config_to_string(const _z_str_intmap_t *s, const _z_string_t *proto) { // Call the right configuration parser depending on the protocol _z_string_t cmp_str = _z_string_null(); #if Z_FEATURE_LINK_TCP == 1 cmp_str = _z_string_alias_str(TCP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tcp_config_to_str(s); } #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 cmp_str = _z_string_alias_str(UDP_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_udp_config_to_str(s); } #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 cmp_str = _z_string_alias_str(BT_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_bt_config_to_str(s); } #endif #if Z_FEATURE_LINK_SERIAL == 1 cmp_str = _z_string_alias_str(SERIAL_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_serial_config_to_str(s); } #endif #if Z_FEATURE_LINK_WS == 1 cmp_str = _z_string_alias_str(WS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_ws_config_to_str(s); } #endif #if Z_FEATURE_LINK_TLS == 1 cmp_str = _z_string_alias_str(TLS_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_tls_config_to_str(s); } #endif cmp_str = _z_string_alias_str(RAWETH_SCHEMA); if (_z_string_equals(proto, &cmp_str)) { return _z_raweth_config_to_str(s); } return NULL; } z_result_t _z_endpoint_from_string(_z_endpoint_t *ep, const _z_string_t *str) { _z_endpoint_init(ep); _Z_CLEAN_RETURN_IF_ERR(_z_locator_from_string(&ep->_locator, str), _z_endpoint_clear(ep)); _Z_CLEAN_RETURN_IF_ERR(_z_endpoint_config_from_string(&ep->_config, str, &ep->_locator._protocol), _z_endpoint_clear(ep)); return _Z_RES_OK; } _z_string_t _z_endpoint_to_string(const _z_endpoint_t *endpoint) { _z_string_t ret = _z_string_null(); // Retrieve locator _z_string_t locator = _z_locator_to_string(&endpoint->_locator); if (!_z_string_check(&locator)) { return _z_string_null(); } size_t curr_len = _z_string_len(&locator); // Retrieve config char *config = _z_endpoint_config_to_string(&endpoint->_config, &endpoint->_locator._protocol); size_t config_len = 0; if (config != NULL) { config_len = strlen(config); curr_len += config_len; } // Reconstruct the endpoint as a string ret = _z_string_preallocate(curr_len); if (!_z_string_check(&ret)) { // cppcheck-suppress misra-c2012-17.3 _z_string_clear(&locator); if (config != NULL) { // cppcheck-suppress misra-c2012-17.3 z_free(config); } return ret; } // Copy locator char *curr_dst = (char *)_z_string_data(&ret); memcpy(curr_dst, _z_string_data(&locator), _z_string_len(&locator)); curr_dst = _z_ptr_char_offset(curr_dst, (ptrdiff_t)_z_string_len(&locator)); // Copy config if (config != NULL) { memcpy(curr_dst, config, config_len); // cppcheck-suppress misra-c2012-17.3 z_free(config); } // Clean up _z_string_clear(&locator); return ret; } ================================================ FILE: src/link/link.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/link.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/config/raweth.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/utils/logging.h" z_result_t _z_open_socket(const _z_string_t *locator, const _z_config_t *session_cfg, _z_sys_net_socket_t *socket) { #if Z_FEATURE_LINK_TLS != 1 _ZP_UNUSED(session_cfg); #endif _z_endpoint_t ep; z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_endpoint_from_string(&ep, locator)); if (_z_endpoint_tcp_valid(&ep) == _Z_RES_OK) { ret = _z_new_peer_tcp(&ep, socket); #if Z_FEATURE_LINK_TLS == 1 } else if (_z_endpoint_tls_valid(&ep) == _Z_RES_OK) { ret = _z_new_peer_tls(&ep, socket, session_cfg); #endif } else { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN); ret = _Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN; } _z_endpoint_clear(&ep); return ret; } z_result_t _z_open_link(_z_link_t *zl, const _z_string_t *locator, const _z_config_t *session_cfg) { #if Z_FEATURE_LINK_TLS != 1 _ZP_UNUSED(session_cfg); #endif z_result_t ret = _Z_RES_OK; _z_endpoint_t ep; ret = _z_endpoint_from_string(&ep, locator); if (ret == _Z_RES_OK) { // Create transport link if (_z_endpoint_tcp_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_tcp(zl, &ep); } else #if Z_FEATURE_LINK_UDP_UNICAST == 1 if (_z_endpoint_udp_unicast_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_udp_unicast(zl, ep); } else #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 if (_z_endpoint_bt_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_bt(zl, ep); } else #endif #if Z_FEATURE_LINK_SERIAL == 1 if (_z_endpoint_serial_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_serial(zl, ep); } else #endif #if Z_FEATURE_LINK_WS == 1 if (_z_endpoint_ws_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_ws(zl, &ep); } else #endif #if Z_FEATURE_LINK_TLS == 1 if (_z_endpoint_tls_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_tls(zl, &ep, session_cfg); } else #endif { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN); ret = _Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN; } if (ret == _Z_RES_OK) { // Open transport link for communication if (zl->_open_f(zl) != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_FAILED); ret = _Z_ERR_TRANSPORT_OPEN_FAILED; _z_link_clear(zl); } } else { _z_endpoint_clear(&ep); } } else { _z_endpoint_clear(&ep); _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } return ret; } z_result_t _z_listen_link(_z_link_t *zl, const _z_string_t *locator, const _z_config_t *session_cfg) { #if Z_FEATURE_LINK_TLS != 1 _ZP_UNUSED(session_cfg); #endif z_result_t ret = _Z_RES_OK; _z_endpoint_t ep; ret = _z_endpoint_from_string(&ep, locator); if (ret == _Z_RES_OK) { // Create transport link if (_z_endpoint_tcp_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_tcp(zl, &ep); } else #if Z_FEATURE_LINK_TLS == 1 if (_z_endpoint_tls_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_tls(zl, &ep, session_cfg); } else #endif #if Z_FEATURE_LINK_UDP_MULTICAST == 1 if (_z_endpoint_udp_multicast_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_udp_multicast(zl, ep); } else #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 if (_z_endpoint_bt_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_bt(zl, ep); } else #endif if (_z_endpoint_raweth_valid(&ep) == _Z_RES_OK) { ret = _z_new_link_raweth(zl, ep); } else { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN); ret = _Z_ERR_CONFIG_LOCATOR_SCHEMA_UNKNOWN; } if (ret == _Z_RES_OK) { // Open transport link for listening if (zl->_listen_f(zl) != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_FAILED); ret = _Z_ERR_TRANSPORT_OPEN_FAILED; _z_link_clear(zl); } } else { _z_endpoint_clear(&ep); } } else { _z_endpoint_clear(&ep); _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } return ret; } void _z_link_clear(_z_link_t *l) { if (l->_close_f != NULL) { l->_close_f(l); } if (l->_free_f != NULL) { l->_free_f(l); } _z_endpoint_clear(&l->_endpoint); } void _z_link_free(_z_link_t **l) { _z_link_t *ptr = *l; if (ptr != NULL) { _z_link_clear(ptr); z_free(ptr); *l = NULL; } } size_t _z_link_recv_zbuf(const _z_link_t *link, _z_zbuf_t *zbf, _z_slice_t *addr) { size_t rb = link->_read_f(link, _z_zbuf_get_wptr(zbf), _z_zbuf_space_left(zbf), addr); if (rb != SIZE_MAX) { _z_zbuf_set_wpos(zbf, _z_zbuf_get_wpos(zbf) + rb); } return rb; } size_t _z_link_recv_exact_zbuf(const _z_link_t *link, _z_zbuf_t *zbf, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { size_t rb = link->_read_exact_f(link, _z_zbuf_get_wptr(zbf), len, addr, socket); if (rb != SIZE_MAX) { _z_zbuf_set_wpos(zbf, _z_zbuf_get_wpos(zbf) + rb); } return rb; } size_t _z_link_socket_recv_zbuf(const _z_link_t *link, _z_zbuf_t *zbf, const _z_sys_net_socket_t socket) { size_t rb = link->_read_socket_f(socket, _z_zbuf_get_wptr(zbf), _z_zbuf_space_left(zbf)); if (rb != SIZE_MAX) { _z_zbuf_set_wpos(zbf, _z_zbuf_get_wpos(zbf) + rb); } return rb; } z_result_t _z_link_send_wbuf(const _z_link_t *link, const _z_wbuf_t *wbf, _z_sys_net_socket_t *socket) { z_result_t ret = _Z_RES_OK; bool link_is_streamed = link->_cap._flow == Z_LINK_CAP_FLOW_STREAM; for (size_t i = 0; (i < _z_wbuf_len_iosli(wbf)) && (ret == _Z_RES_OK); i++) { _z_slice_t bs = _z_iosli_to_bytes(_z_wbuf_get_iosli(wbf, i)); size_t n = bs.len; do { size_t wb = link->_write_f(link, bs.start, n, socket); if ((wb == SIZE_MAX) || (wb > n)) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; break; } if (link_is_streamed && wb != n) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; break; } n = n - wb; bs.start = bs.start + (bs.len - n); } while (n > (size_t)0); } return ret; } const _z_sys_net_socket_t *_z_link_get_socket(const _z_link_t *link) { switch (link->_type) { #if Z_FEATURE_LINK_TCP == 1 case _Z_LINK_TYPE_TCP: return &link->_socket._tcp._sock; #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 case _Z_LINK_TYPE_UDP: return &link->_socket._udp._sock; #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 case _Z_LINK_TYPE_BT: return &link->_socket._bt._sock; #endif #if Z_FEATURE_LINK_SERIAL == 1 case _Z_LINK_TYPE_SERIAL: return &link->_socket._serial._sock; #endif #if Z_FEATURE_LINK_WS == 1 case _Z_LINK_TYPE_WS: return &link->_socket._ws._sock; #endif #if Z_FEATURE_LINK_TLS == 1 case _Z_LINK_TYPE_TLS: return &link->_socket._tls._sock; #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_LINK_TYPE_RAWETH: return &link->_socket._raweth._sock; #endif default: _Z_INFO("Unknown link type"); return NULL; } } ================================================ FILE: src/link/multicast/bt.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/link/config/bt.h" #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/bt.h" #if Z_FEATURE_LINK_BLUETOOTH == 1 #define SPP_MAXIMUM_PAYLOAD 128 z_result_t _z_endpoint_bt_valid(_z_endpoint_t *ep) { _z_string_t bt_str = _z_string_alias_str(BT_SCHEMA); if (!_z_string_equals(&ep->_locator._protocol, &bt_str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } if (_z_string_len(&ep->_locator._address) == (size_t)0) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } return _Z_RES_OK; } static char *__z_convert_address_bt(_z_string_t *address) { char *ret = (char *)z_malloc(_z_string_len(address) + 1); if (ret != NULL) { _z_str_n_copy(ret, _z_string_data(address), _z_string_len(address) + 1); } return ret; } z_result_t _z_f_link_open_bt(_z_link_t *self) { const char *mode_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_MODE_KEY); uint8_t mode = (strcmp(mode_str, "master") == 0) ? _Z_BT_MODE_MASTER : _Z_BT_MODE_SLAVE; const char *profile_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_PROFILE_KEY); uint8_t profile = (strcmp(profile_str, "spp") == 0) ? _Z_BT_PROFILE_SPP : _Z_BT_PROFILE_UNSUPPORTED; uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } self->_socket._bt._gname = __z_convert_address_bt(&self->_endpoint._locator._address); return _z_open_bt(&self->_socket._bt._sock, self->_socket._bt._gname, mode, profile, tout); } z_result_t _z_f_link_listen_bt(_z_link_t *self) { const char *mode_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_MODE_KEY); uint8_t mode = (strcmp(mode_str, "master") == 0) ? _Z_BT_MODE_MASTER : _Z_BT_MODE_SLAVE; const char *profile_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_PROFILE_KEY); uint8_t profile = (strcmp(profile_str, "spp") == 0) ? _Z_BT_PROFILE_SPP : _Z_BT_PROFILE_UNSUPPORTED; uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&self->_endpoint._config, BT_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } self->_socket._bt._gname = __z_convert_address_bt(&self->_endpoint._locator._address); return _z_listen_bt(&self->_socket._bt._sock, self->_socket._bt._gname, mode, profile, tout); } void _z_f_link_close_bt(_z_link_t *self) { _z_close_bt(&self->_socket._bt._sock); } void _z_f_link_free_bt(_z_link_t *self) { _ZP_UNUSED(self); } size_t _z_f_link_write_bt(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); return _z_send_bt(self->_socket._bt._sock, ptr, len); } size_t _z_f_link_write_all_bt(const _z_link_t *self, const uint8_t *ptr, size_t len) { return _z_send_bt(self->_socket._bt._sock, ptr, len); } size_t _z_f_link_read_bt(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { size_t rb = _z_read_bt(self->_socket._bt._sock, ptr, len); if ((rb > (size_t)0) && (addr != NULL)) { addr->len = strlen(self->_socket._bt._gname); (void)memcpy((uint8_t *)addr->start, self->_socket._bt._gname, strlen(self->_socket._bt._gname)); } return rb; } size_t _z_f_link_read_exact_bt(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); size_t rb = _z_read_exact_bt(self->_socket._bt._sock, ptr, len); if ((rb == len) && (addr != NULL)) { addr->len = strlen(self->_socket._bt._gname); (void)memcpy((uint8_t *)addr->start, self->_socket._bt._gname, strlen(self->_socket._bt._gname)); } return rb; } uint16_t _z_get_link_mtu_bt(void) { return SPP_MAXIMUM_PAYLOAD; } z_result_t _z_new_link_bt(_z_link_t *zl, _z_endpoint_t endpoint) { zl->_type = _Z_LINK_TYPE_BT; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_MULTICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_STREAM; zl->_cap._is_reliable = false; zl->_mtu = _z_get_link_mtu_bt(); zl->_endpoint = endpoint; zl->_open_f = _z_f_link_open_bt; zl->_listen_f = _z_f_link_listen_bt; zl->_close_f = _z_f_link_close_bt; zl->_free_f = _z_f_link_free_bt; zl->_write_f = _z_f_link_write_bt; zl->_write_all_f = _z_f_link_write_all_bt; zl->_read_f = _z_f_link_read_bt; zl->_read_exact_f = _z_f_link_read_exact_bt; zl->_read_socket_f = _z_noop_link_read_socket; return _Z_RES_OK; } #endif ================================================ FILE: src/link/multicast/udp.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/udp.h" #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/udp_multicast.h" #include "zenoh-pico/link/transport/udp_unicast.h" #if Z_FEATURE_LINK_UDP_MULTICAST == 1 z_result_t _z_endpoint_udp_multicast_valid(_z_endpoint_t *endpoint) { _z_string_t udp_str = _z_string_alias_str(UDP_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &udp_str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _z_udp_unicast_address_valid(&endpoint->_locator._address); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return ret; } const char *iface = _z_str_intmap_get(&endpoint->_config, UDP_CONFIG_IFACE_KEY); if (iface == NULL) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } return _Z_RES_OK; } z_result_t _z_f_link_open_udp_multicast(_z_link_t *self) { uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } const char *iface = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_IFACE_KEY); return _z_udp_multicast_open(&self->_socket._udp._sock, self->_socket._udp._rep, &self->_socket._udp._lep, tout, iface); } z_result_t _z_f_link_listen_udp_multicast(_z_link_t *self) { z_result_t ret = _Z_RES_OK; const char *iface = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_IFACE_KEY); const char *join = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_JOIN_KEY); ret = _z_udp_multicast_listen(&self->_socket._udp._sock, self->_socket._udp._rep, Z_CONFIG_SOCKET_TIMEOUT, iface, join); ret |= _z_udp_multicast_open(&self->_socket._udp._msock, self->_socket._udp._rep, &self->_socket._udp._lep, Z_CONFIG_SOCKET_TIMEOUT, iface); return ret; } void _z_f_link_close_udp_multicast(_z_link_t *self) { _z_udp_multicast_close(&self->_socket._udp._sock, &self->_socket._udp._msock, self->_socket._udp._rep, self->_socket._udp._lep); } void _z_f_link_free_udp_multicast(_z_link_t *self) { _z_udp_multicast_endpoint_clear(&self->_socket._udp._lep); _z_udp_multicast_endpoint_clear(&self->_socket._udp._rep); } size_t _z_f_link_write_udp_multicast(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); return _z_udp_multicast_write(self->_socket._udp._msock, ptr, len, self->_socket._udp._rep); } size_t _z_f_link_write_all_udp_multicast(const _z_link_t *self, const uint8_t *ptr, size_t len) { return _z_udp_multicast_write(self->_socket._udp._msock, ptr, len, self->_socket._udp._rep); } size_t _z_f_link_read_udp_multicast(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { return _z_udp_multicast_read(self->_socket._udp._sock, ptr, len, self->_socket._udp._lep, addr); } size_t _z_f_link_read_exact_udp_multicast(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); return _z_udp_multicast_read_exact(self->_socket._udp._sock, ptr, len, self->_socket._udp._lep, addr); } uint16_t _z_get_link_mtu_udp_multicast(void) { // @TODO: the return value should change depending on the target platform. return 1450; } z_result_t _z_new_link_udp_multicast(_z_link_t *zl, _z_endpoint_t endpoint) { zl->_type = _Z_LINK_TYPE_UDP; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_MULTICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_DATAGRAM; zl->_cap._is_reliable = false; zl->_mtu = _z_get_link_mtu_udp_multicast(); zl->_endpoint = endpoint; z_result_t ret = _z_udp_multicast_endpoint_init_from_address(&zl->_socket._udp._rep, &endpoint._locator._address); memset(&zl->_socket._udp._lep, 0, sizeof(zl->_socket._udp._lep)); zl->_open_f = _z_f_link_open_udp_multicast; zl->_listen_f = _z_f_link_listen_udp_multicast; zl->_close_f = _z_f_link_close_udp_multicast; zl->_free_f = _z_f_link_free_udp_multicast; zl->_write_f = _z_f_link_write_udp_multicast; zl->_write_all_f = _z_f_link_write_all_udp_multicast; zl->_read_f = _z_f_link_read_udp_multicast; zl->_read_exact_f = _z_f_link_read_exact_udp_multicast; zl->_read_socket_f = _z_noop_link_read_socket; return ret; } #endif ================================================ FILE: src/link/transport/bt/bt_arduino_esp32.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_ARDUINO_ESP32) && Z_FEATURE_LINK_BLUETOOTH == 1 #include extern "C" { #include #include "zenoh-pico/link/transport/bt.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_open_bt(_z_sys_net_socket_t *sock, const char *gname, uint8_t mode, uint8_t profile, uint32_t tout) { z_result_t ret = _Z_RES_OK; if (profile == _Z_BT_PROFILE_SPP) { sock->_bts = new BluetoothSerial(); if (mode == _Z_BT_MODE_SLAVE) { sock->_bts->begin(gname, false); } else if (mode == _Z_BT_MODE_MASTER) { sock->_bts->begin(gname, true); uint8_t connected = sock->_bts->connect(gname); if (!connected) { while (!sock->_bts->connected(tout)) { ZP_ASM_NOP; } } } else { delete sock->_bts; _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_listen_bt(_z_sys_net_socket_t *sock, const char *gname, uint8_t mode, uint8_t profile, uint32_t tout) { z_result_t ret = _Z_RES_OK; if (profile == _Z_BT_PROFILE_SPP) { sock->_bts = new BluetoothSerial(); if (mode == _Z_BT_MODE_SLAVE) { sock->_bts->begin(gname, false); } else if (mode == _Z_BT_MODE_MASTER) { sock->_bts->begin(gname, true); uint8_t connected = sock->_bts->connect(gname); if (!connected) { while (!sock->_bts->connected(tout)) { ZP_ASM_NOP; } } } else { delete sock->_bts; _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_close_bt(_z_sys_net_socket_t *sock) { sock->_bts->end(); delete sock->_bts; } size_t _z_read_bt(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t i = 0; for (i = 0; i < len; i++) { // flawfinder: ignore int c = sock._bts->read(); if (c == -1) { delay(1); break; } ptr[i] = (uint8_t)c; } return i; } size_t _z_read_exact_bt(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_bt(sock, pos, len - n); if (rb == SIZE_MAX) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_bt(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { sock._bts->write(ptr, len); return len; } } // extern "C" #endif ================================================ FILE: src/link/transport/common/address.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/config.h" #if defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD) #include #endif #if defined(ZENOH_FREERTOS_LWIP) #include "lwip/inet.h" #endif #if defined(ZENOH_ZEPHYR) #include #endif #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/utils/logging.h" #if defined(ZENOH_WINDOWS) || defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD) || \ defined(ZENOH_FREERTOS_LWIP) || defined(ZENOH_ZEPHYR) static z_result_t _z_ipv4_port_to_endpoint(const uint8_t *address, uint16_t port, char *dst, size_t dst_len) { char ip[INET_ADDRSTRLEN] = {0}; int written = -1; #if defined(ZENOH_ZEPHYR) if (zsock_inet_ntop(AF_INET, address, ip, sizeof(ip)) == NULL) { #else if (inet_ntop(AF_INET, address, ip, sizeof(ip)) == NULL) { #endif _Z_ERROR_RETURN(_Z_ERR_GENERIC); } written = snprintf(dst, dst_len, "%s:%u", ip, (unsigned)port); if ((written < 0) || ((size_t)written >= dst_len)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_ipv6_port_to_endpoint(const uint8_t *address, uint16_t port, char *dst, size_t dst_len) { char ip[INET6_ADDRSTRLEN] = {0}; int written = -1; #if defined(ZENOH_ZEPHYR) if (zsock_inet_ntop(AF_INET6, address, ip, sizeof(ip)) == NULL) { #else if (inet_ntop(AF_INET6, address, ip, sizeof(ip)) == NULL) { #endif _Z_ERROR_RETURN(_Z_ERR_GENERIC); } written = snprintf(dst, dst_len, "[%s]:%u", ip, (unsigned)port); if ((written < 0) || ((size_t)written >= dst_len)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_ip_port_to_endpoint(const uint8_t *address, size_t address_len, uint16_t port, char *dst, size_t dst_len) { if (address == NULL || dst == NULL || dst_len == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (address_len == sizeof(uint32_t)) { return _z_ipv4_port_to_endpoint(address, port, dst, dst_len); } else if (address_len == 16) { return _z_ipv6_port_to_endpoint(address, port, dst, dst_len); } else { _Z_ERROR_RETURN(_Z_ERR_INVALID); } } #else z_result_t _z_ip_port_to_endpoint(const uint8_t *address, size_t address_len, uint16_t port, char *dst, size_t dst_len) { _ZP_UNUSED(address); _ZP_UNUSED(address_len); _ZP_UNUSED(port); _ZP_UNUSED(dst); _ZP_UNUSED(dst_len); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif ================================================ FILE: src/link/transport/common/endpoints.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_WINDOWS) #include #include #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" static z_result_t _z_sockaddr_to_endpoint(const SOCKADDR *addr, char *dst, size_t dst_len) { if (addr->sa_family == AF_INET) { const SOCKADDR_IN *addr4 = (const SOCKADDR_IN *)addr; const uint8_t *bytes = (const uint8_t *)&addr4->sin_addr; return _z_ip_port_to_endpoint(bytes, sizeof(addr4->sin_addr), ntohs(addr4->sin_port), dst, dst_len); } else if (addr->sa_family == AF_INET6) { const SOCKADDR_IN6 *addr6 = (const SOCKADDR_IN6 *)addr; const uint8_t *bytes = (const uint8_t *)&addr6->sin6_addr; return _z_ip_port_to_endpoint(bytes, sizeof(addr6->sin6_addr), ntohs(addr6->sin6_port), dst, dst_len); } else { _Z_ERROR_RETURN(_Z_ERR_INVALID); } } z_result_t _z_socket_get_endpoints(const _z_sys_net_socket_t *sock, char *local, size_t local_len, char *remote, size_t remote_len) { SOCKADDR_STORAGE local_addr = {0}; SOCKADDR_STORAGE remote_addr = {0}; int local_addr_len = sizeof(local_addr); int remote_addr_len = sizeof(remote_addr); SOCKET fd; if (sock == NULL || local == NULL || remote == NULL || local_len == 0 || remote_len == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } fd = sock->_sock._fd; if (fd == INVALID_SOCKET) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (getsockname(fd, (SOCKADDR *)&local_addr, &local_addr_len) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (getpeername(fd, (SOCKADDR *)&remote_addr, &remote_addr_len) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _Z_RETURN_IF_ERR(_z_sockaddr_to_endpoint((const SOCKADDR *)&local_addr, local, local_len)); _Z_RETURN_IF_ERR(_z_sockaddr_to_endpoint((const SOCKADDR *)&remote_addr, remote, remote_len)); return _Z_RES_OK; } #elif defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD) || defined(ZENOH_ZEPHYR) #include #if defined(ZENOH_ZEPHYR) #include #include #include #else #include #include #endif #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" static z_result_t _z_sockaddr_to_endpoint(const struct sockaddr *addr, char *dst, size_t dst_len) { if (addr->sa_family == AF_INET) { const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; const uint8_t *bytes = (const uint8_t *)&addr4->sin_addr; return _z_ip_port_to_endpoint(bytes, sizeof(addr4->sin_addr), ntohs(addr4->sin_port), dst, dst_len); } else if (addr->sa_family == AF_INET6) { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; const uint8_t *bytes = (const uint8_t *)&addr6->sin6_addr; return _z_ip_port_to_endpoint(bytes, sizeof(addr6->sin6_addr), ntohs(addr6->sin6_port), dst, dst_len); } else { _Z_ERROR_RETURN(_Z_ERR_INVALID); } } z_result_t _z_socket_get_endpoints(const _z_sys_net_socket_t *sock, char *local, size_t local_len, char *remote, size_t remote_len) { struct sockaddr_storage local_addr = {0}; struct sockaddr_storage remote_addr = {0}; socklen_t local_addr_len = sizeof(local_addr); socklen_t remote_addr_len = sizeof(remote_addr); if (sock == NULL || local == NULL || remote == NULL || local_len == 0 || remote_len == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (sock->_fd < 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (getsockname(sock->_fd, (struct sockaddr *)&local_addr, &local_addr_len) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (getpeername(sock->_fd, (struct sockaddr *)&remote_addr, &remote_addr_len) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _Z_RETURN_IF_ERR(_z_sockaddr_to_endpoint((const struct sockaddr *)&local_addr, local, local_len)); _Z_RETURN_IF_ERR(_z_sockaddr_to_endpoint((const struct sockaddr *)&remote_addr, remote, remote_len)); return _Z_RES_OK; } #else #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/utils/logging.h" z_result_t _z_socket_get_endpoints(const _z_sys_net_socket_t *sock, char *local, size_t local_len, char *remote, size_t remote_len) { _ZP_UNUSED(sock); _ZP_UNUSED(local); _ZP_UNUSED(local_len); _ZP_UNUSED(remote); _ZP_UNUSED(remote_len); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif ================================================ FILE: src/link/transport/serial/tty_posix.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if Z_FEATURE_LINK_SERIAL == 1 && (defined(ZENOH_LINUX) || defined(ZENOH_MACOS) || defined(ZENOH_BSD)) #include #include #include #include #include #include "zenoh-pico/utils/logging.h" static speed_t _z_posix_tty_baudrate_to_speed(uint32_t baudrate) { switch (baudrate) { case 1200: return B1200; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; #ifdef B57600 case 57600: return B57600; #endif #ifdef B115200 case 115200: return B115200; #endif #ifdef B230400 case 230400: return B230400; #endif default: return (speed_t)0; } } static z_result_t _z_posix_tty_open_impl(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { if (sock == NULL || dev == NULL || baudrate == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } speed_t speed = _z_posix_tty_baudrate_to_speed(baudrate); if (speed == (speed_t)0) { _Z_ERROR("Unsupported serial baudrate: %u", (unsigned)baudrate); _Z_ERROR_RETURN(_Z_ERR_INVALID); } // flawfinder: ignore int fd = open(dev, O_RDWR | O_NOCTTY); if (fd < 0) { _Z_ERROR("Failed to open serial device %s: errno=%d", dev, errno); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } struct termios tty; if (tcgetattr(fd, &tty) != 0) { close(fd); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } cfmakeraw(&tty); tty.c_cflag |= (tcflag_t)(CLOCAL | CREAD); #ifdef CRTSCTS tty.c_cflag &= (tcflag_t)~CRTSCTS; #endif tty.c_cflag &= (tcflag_t)~CSTOPB; tty.c_cflag &= (tcflag_t)~PARENB; tty.c_cflag &= (tcflag_t)~CSIZE; tty.c_cflag |= (tcflag_t)CS8; tty.c_cc[VMIN] = 1; tty.c_cc[VTIME] = 0; if (cfsetispeed(&tty, speed) != 0 || cfsetospeed(&tty, speed) != 0) { close(fd); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (tcsetattr(fd, TCSANOW, &tty) != 0) { close(fd); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (tcflush(fd, TCIOFLUSH) != 0) { close(fd); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_fd = fd; return _Z_RES_OK; } static z_result_t _z_posix_tty_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_INVALID); } static z_result_t _z_posix_tty_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_posix_tty_open_impl(sock, dev, baudrate); } static z_result_t _z_posix_tty_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_posix_tty_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_posix_tty_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_posix_tty_open_impl(sock, dev, baudrate); } static void _z_posix_tty_close(_z_sys_net_socket_t *sock) { if (sock != NULL && sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } static size_t _z_posix_tty_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t count = len > (size_t)SSIZE_MAX ? (size_t)SSIZE_MAX : len; // flawfinder: ignore ssize_t rb = read(sock._fd, ptr, count); if (rb <= 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_posix_tty_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { size_t count = len > (size_t)SSIZE_MAX ? (size_t)SSIZE_MAX : len; ssize_t wb = write(sock._fd, ptr, count); if (wb <= 0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_posix_tty_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_posix_tty_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_posix_tty_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_posix_tty_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_posix_tty_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_posix_tty_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_posix_tty_write(sock, ptr, len); } #endif /* Z_FEATURE_LINK_SERIAL == 1 && POSIX */ ================================================ FILE: src/link/transport/serial/uart_arduino_esp32.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_ARDUINO_ESP32) #include #include extern "C" { #include "zenoh-pico/link/transport/serial.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_LINK_SERIAL == 1 static z_result_t _z_uart_arduino_esp32_map_pins(uint32_t txpin, uint32_t rxpin, uint8_t *uart) { if (txpin == 1 && rxpin == 3) { *uart = 0; } else if (txpin == 10 && rxpin == 9) { *uart = 1; } else if (txpin == 17 && rxpin == 16) { *uart = 2; } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_uart_arduino_esp32_map_dev(const char *dev, uint8_t *uart, uint32_t *txpin, uint32_t *rxpin) { if (strcmp(dev, "UART_0") == 0) { *uart = 0; *txpin = 1; *rxpin = 3; } else if (strcmp(dev, "UART_1") == 0) { *uart = 1; *txpin = 10; *rxpin = 9; } else if (strcmp(dev, "UART_2") == 0) { *uart = 2; *txpin = 17; *rxpin = 16; } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_uart_arduino_esp32_open_impl(_z_sys_net_socket_t *sock, uint8_t uart, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { if (sock == NULL || baudrate == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } // Keep the lines high before the UART is initialized to reduce the initial glitch. pinMode((int)rxpin, INPUT_PULLUP); pinMode((int)txpin, OUTPUT); digitalWrite((int)txpin, HIGH); sock->_serial = new HardwareSerial(uart); if (sock->_serial == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_serial->begin(baudrate); sock->_serial->flush(); return _Z_RES_OK; } static z_result_t _z_uart_arduino_esp32_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { uint8_t uart = 0; _Z_RETURN_IF_ERR(_z_uart_arduino_esp32_map_pins(txpin, rxpin, &uart)); return _z_uart_arduino_esp32_open_impl(sock, uart, txpin, rxpin, baudrate); } static z_result_t _z_uart_arduino_esp32_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { uint8_t uart = 0; uint32_t txpin = 0; uint32_t rxpin = 0; _Z_RETURN_IF_ERR(_z_uart_arduino_esp32_map_dev(dev, &uart, &txpin, &rxpin)); return _z_uart_arduino_esp32_open_impl(sock, uart, txpin, rxpin, baudrate); } static z_result_t _z_uart_arduino_esp32_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_arduino_esp32_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_uart_arduino_esp32_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_arduino_esp32_open_from_dev(sock, dev, baudrate); } static void _z_uart_arduino_esp32_close(_z_sys_net_socket_t *sock) { if (sock != NULL && sock->_serial != NULL) { sock->_serial->end(); delete sock->_serial; sock->_serial = NULL; } } static size_t _z_uart_arduino_esp32_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t rb = 0; while (rb < len) { while (sock._serial->available() < 1) { z_sleep_ms(1); } // flawfinder: ignore int byte = sock._serial->read(); if (byte < 0) { return SIZE_MAX; } ptr[rb++] = (uint8_t)byte; } return rb; } static size_t _z_uart_arduino_esp32_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { size_t wb = sock._serial->write(ptr, len); if (wb == 0) { return SIZE_MAX; } return wb; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_arduino_esp32_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_arduino_esp32_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_arduino_esp32_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_arduino_esp32_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_uart_arduino_esp32_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_uart_arduino_esp32_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_uart_arduino_esp32_write(sock, ptr, len); } #endif /* Z_FEATURE_LINK_SERIAL == 1 */ } // extern "C" #endif /* defined(ZENOH_ARDUINO_ESP32) */ ================================================ FILE: src/link/transport/serial/uart_espidf.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_ESPIDF) #include #include #include #include #include "zenoh-pico/utils/logging.h" static z_result_t _z_espidf_uart_map_dev(const char *dev, uart_port_t *serial, uint32_t *txpin, uint32_t *rxpin) { if (strcmp(dev, "UART_0") == 0) { *serial = UART_NUM_0; *txpin = 1; *rxpin = 3; } else if (strcmp(dev, "UART_1") == 0) { *serial = UART_NUM_1; *txpin = 10; *rxpin = 9; } else if (strcmp(dev, "UART_2") == 0) { *serial = UART_NUM_2; *txpin = 17; *rxpin = 16; } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_espidf_uart_map_pins(uint32_t txpin, uint32_t rxpin, uart_port_t *serial) { if (txpin == 1 && rxpin == 3) { *serial = UART_NUM_0; } else if (txpin == 10 && rxpin == 9) { *serial = UART_NUM_1; } else if (txpin == 17 && rxpin == 16) { *serial = UART_NUM_2; } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_espidf_uart_open_impl(_z_sys_net_socket_t *sock, uart_port_t serial, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { if (sock == NULL || baudrate == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } sock->_serial = serial; const uart_config_t config = { .baud_rate = (int)baudrate, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .data_bits = UART_DATA_8_BITS, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, }; if (uart_param_config(sock->_serial, &config) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (uart_set_pin(sock->_serial, (int)txpin, (int)rxpin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } const int uart_buffer_size = 1024 * 2; QueueHandle_t uart_queue = NULL; if (uart_driver_install(sock->_serial, uart_buffer_size, 0, 100, &uart_queue, 0) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } uart_flush_input(sock->_serial); return _Z_RES_OK; } static z_result_t _z_espidf_uart_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { uart_port_t serial = UART_NUM_0; _Z_RETURN_IF_ERR(_z_espidf_uart_map_pins(txpin, rxpin, &serial)); return _z_espidf_uart_open_impl(sock, serial, txpin, rxpin, baudrate); } static z_result_t _z_espidf_uart_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { uart_port_t serial = UART_NUM_0; uint32_t txpin = 0; uint32_t rxpin = 0; _Z_RETURN_IF_ERR(_z_espidf_uart_map_dev(dev, &serial, &txpin, &rxpin)); return _z_espidf_uart_open_impl(sock, serial, txpin, rxpin, baudrate); } static z_result_t _z_espidf_uart_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_espidf_uart_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_espidf_uart_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_espidf_uart_open_from_dev(sock, dev, baudrate); } static void _z_espidf_uart_close(_z_sys_net_socket_t *sock) { if (sock != NULL) { uart_wait_tx_done(sock->_serial, 1000 / portTICK_PERIOD_MS); uart_flush(sock->_serial); uart_driver_delete(sock->_serial); } } static size_t _z_espidf_uart_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { int rb = uart_read_bytes(sock._serial, ptr, len, 1000 / portTICK_PERIOD_MS); if (rb <= 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_espidf_uart_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { int wb = uart_write_bytes(sock._serial, ptr, len); if (wb <= 0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_espidf_uart_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_espidf_uart_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_espidf_uart_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_espidf_uart_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_espidf_uart_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_espidf_uart_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_espidf_uart_write(sock, ptr, len); } #endif /* Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_ESPIDF) */ ================================================ FILE: src/link/transport/serial/uart_flipper.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if defined(ZENOH_FLIPPER) && (Z_FEATURE_LINK_SERIAL == 1) #include #include "zenoh-pico/utils/logging.h" static void _z_uart_flipper_received_byte_callback(FuriHalSerialHandle *handle, FuriHalSerialRxEvent event, void *context) { if (context == NULL) { return; } if (event == FuriHalSerialRxEventData) { uint8_t data = furi_hal_serial_async_rx(handle); furi_stream_buffer_send((FuriStreamBuffer *)context, &data, 1, FLIPPER_SERIAL_STREAM_TRIGGERED_LEVEL); } } static z_result_t _z_uart_flipper_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_uart_flipper_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { if (furi_hal_serial_control_is_busy(FuriHalSerialIdUsart)) { _Z_ERROR("Serial port is busy"); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_OPEN_FAILED); } FuriHalSerialId sid; if (!strcmp(dev, "usart")) { sid = FuriHalSerialIdUsart; } else if (!strcmp(dev, "lpuart")) { sid = FuriHalSerialIdLpuart; } else { _Z_ERROR("Unknown serial port device: %s", dev); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_OPEN_FAILED); } sock->_serial = furi_hal_serial_control_acquire(sid); if (sock->_serial == NULL) { _Z_ERROR("Serial port control acquire failed"); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_OPEN_FAILED); } furi_hal_serial_init(sock->_serial, baudrate); sock->_rx_stream = furi_stream_buffer_alloc(FLIPPER_SERIAL_STREAM_BUFFER_SIZE, FLIPPER_SERIAL_STREAM_TRIGGERED_LEVEL); if (sock->_rx_stream == NULL) { _Z_ERROR("Serial stream buffer allocation failed"); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NO_SPACE); } furi_hal_serial_async_rx_start(sock->_serial, _z_uart_flipper_received_byte_callback, sock->_rx_stream, false); _Z_DEBUG("Serial port opened: %s (%li)", dev, baudrate); return _Z_RES_OK; } static z_result_t _z_uart_flipper_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_uart_flipper_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static void _z_uart_flipper_close(_z_sys_net_socket_t *sock) { if (sock->_serial != NULL) { furi_hal_serial_async_rx_stop(sock->_serial); furi_hal_serial_deinit(sock->_serial); furi_hal_serial_control_release(sock->_serial); z_sleep_ms(FLIPPER_SERIAL_TIMEOUT_MS * 2); furi_stream_buffer_free(sock->_rx_stream); sock->_serial = NULL; sock->_rx_stream = NULL; } _Z_DEBUG("Serial port closed"); } static size_t _z_uart_flipper_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { for (size_t i = 0; i < len; i++) { size_t received = furi_stream_buffer_receive(sock._rx_stream, &ptr[i], 1, FLIPPER_SERIAL_TIMEOUT_MS); if (received != 1) { return SIZE_MAX; } } return len; } static size_t _z_uart_flipper_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { furi_hal_serial_tx(sock._serial, ptr, len); furi_hal_serial_tx_wait_complete(sock._serial); return len; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_flipper_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_flipper_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_flipper_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_flipper_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_uart_flipper_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_uart_flipper_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_uart_flipper_write(sock, ptr, len); } #endif /* defined(ZENOH_FLIPPER) && (Z_FEATURE_LINK_SERIAL == 1) */ ================================================ FILE: src/link/transport/serial/uart_mbed.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_MBED) #include extern "C" { #include #include "zenoh-pico/link/transport/serial.h" #include "zenoh-pico/utils/logging.h" static z_result_t _z_uart_mbed_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { sock->_serial = new BufferedSerial(PinName(txpin), PinName(rxpin), baudrate); if (sock->_serial == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_serial->set_format(8, BufferedSerial::None, 1); sock->_serial->enable_input(true); sock->_serial->enable_output(true); return _Z_RES_OK; } static z_result_t _z_uart_mbed_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_uart_mbed_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_mbed_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_uart_mbed_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static void _z_uart_mbed_close(_z_sys_net_socket_t *sock) { if (sock != NULL && sock->_serial != NULL) { delete sock->_serial; sock->_serial = NULL; } } static size_t _z_uart_mbed_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { // flawfinder: ignore ssize_t rb = sock._serial->read(ptr, len); if (rb <= 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_uart_mbed_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { ssize_t wb = sock._serial->write(ptr, len); if (wb <= 0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_mbed_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_mbed_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_mbed_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_mbed_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_uart_mbed_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_uart_mbed_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_uart_mbed_write(sock, ptr, len); } } // extern "C" #endif /* defined(ZENOH_MBED) */ ================================================ FILE: src/link/transport/serial/uart_rpi_pico.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if defined(ZENOH_RPI_PICO) && (Z_FEATURE_LINK_SERIAL == 1) #include #include "zenoh-pico/utils/logging.h" typedef struct { uint32_t tx_pin; uint32_t rx_pin; uart_inst_t *uart; const char *alias; } _z_uart_rpi_pico_pins_t; static const _z_uart_rpi_pico_pins_t _z_uart_rpi_pico_allowed_pins[] = {{0, 1, uart0, "uart0_0"}, {4, 5, uart1, "uart1_0"}, {8, 9, uart1, "uart1_1"}, {12, 13, uart0, "uart0_1"}, {16, 17, uart0, "uart0_2"}}; #define _Z_UART_RPI_PICO_NUM_PIN_COMBINATIONS \ (sizeof(_z_uart_rpi_pico_allowed_pins) / sizeof(_z_uart_rpi_pico_allowed_pins[0])) static void _z_uart_rpi_pico_open_impl(uart_inst_t *uart, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { uart_init(uart, baudrate); gpio_set_function(txpin, UART_FUNCSEL_NUM(uart, txpin)); gpio_set_function(rxpin, UART_FUNCSEL_NUM(uart, rxpin)); uart_set_format(uart, 8, 1, UART_PARITY_NONE); } static z_result_t _z_uart_rpi_pico_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { if (sock == NULL || baudrate == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } sock->_serial = NULL; for (size_t i = 0; i < _Z_UART_RPI_PICO_NUM_PIN_COMBINATIONS; i++) { if (_z_uart_rpi_pico_allowed_pins[i].tx_pin == txpin && _z_uart_rpi_pico_allowed_pins[i].rx_pin == rxpin) { sock->_serial = _z_uart_rpi_pico_allowed_pins[i].uart; break; } } if (sock->_serial == NULL) { _Z_ERROR("invalid pin combination"); _Z_ERROR_RETURN(_Z_ERR_INVALID); } _z_uart_rpi_pico_open_impl(sock->_serial, txpin, rxpin, baudrate); return _Z_RES_OK; } static z_result_t _z_uart_rpi_pico_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { sock->_serial = NULL; #if Z_FEATURE_LINK_SERIAL_USB == 1 if (strcmp("usb", dev) == 0) { _z_usb_uart_init(); return _Z_RES_OK; } #endif uint32_t txpin = 0; uint32_t rxpin = 0; for (size_t i = 0; i < _Z_UART_RPI_PICO_NUM_PIN_COMBINATIONS; i++) { if (strcmp(_z_uart_rpi_pico_allowed_pins[i].alias, dev) == 0) { sock->_serial = _z_uart_rpi_pico_allowed_pins[i].uart; txpin = _z_uart_rpi_pico_allowed_pins[i].tx_pin; rxpin = _z_uart_rpi_pico_allowed_pins[i].rx_pin; break; } } if (sock->_serial == NULL) { _Z_ERROR("invalid device name"); _Z_ERROR_RETURN(_Z_ERR_INVALID); } _z_uart_rpi_pico_open_impl(sock->_serial, txpin, rxpin, baudrate); return _Z_RES_OK; } static z_result_t _z_uart_rpi_pico_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static z_result_t _z_uart_rpi_pico_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_uart_rpi_pico_close(_z_sys_net_socket_t *sock) { if (sock->_serial != NULL) { uart_deinit(sock->_serial); } else { #if Z_FEATURE_LINK_SERIAL_USB == 1 _z_usb_uart_deinit(); #endif } } static size_t _z_uart_rpi_pico_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { for (size_t i = 0; i < len; i++) { #if Z_FEATURE_LINK_SERIAL_USB == 1 ptr[i] = (sock._serial == NULL) ? _z_usb_uart_getc() : uart_getc(sock._serial); #else ptr[i] = uart_getc(sock._serial); #endif } return len; } static size_t _z_uart_rpi_pico_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { if (sock._serial == NULL) { #if Z_FEATURE_LINK_SERIAL_USB == 1 _z_usb_uart_write(ptr, (int)len); #endif } else { uart_write_blocking(sock._serial, ptr, len); } return len; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_rpi_pico_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_rpi_pico_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_uart_rpi_pico_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_uart_rpi_pico_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_uart_rpi_pico_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_uart_rpi_pico_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_uart_rpi_pico_write(sock, ptr, len); } #endif /* defined(ZENOH_RPI_PICO) && (Z_FEATURE_LINK_SERIAL == 1) */ ================================================ FILE: src/link/transport/serial/uart_threadx_stm32.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_THREADX_STM32) #include #include #include "hal.h" #include "zenoh-pico/link/transport/serial_protocol.h" #include "zenoh-pico/utils/logging.h" #define RX_DMA_BUFFER_SIZE (_Z_SERIAL_MAX_COBS_BUF_SIZE * 2 + 2) static uint8_t _z_threadx_stm32_dma_buffer[RX_DMA_BUFFER_SIZE]; static uint16_t _z_threadx_stm32_delimiter_offset = 0; static TX_SEMAPHORE _z_threadx_stm32_data_ready_semaphore; static TX_SEMAPHORE _z_threadx_stm32_data_processing_semaphore; static uint8_t _z_threadx_stm32_frame_buffer[_Z_SERIAL_MAX_COBS_BUF_SIZE]; static size_t _z_threadx_stm32_frame_len = 0; static size_t _z_threadx_stm32_frame_offset = 0; static bool _z_threadx_stm32_initialized = false; static uint16_t _z_threadx_stm32_last_dma_offset = 0; extern UART_HandleTypeDef ZENOH_HUART; static z_result_t _z_threadx_stm32_uart_open_impl(void) { if (!_z_threadx_stm32_initialized) { tx_semaphore_create(&_z_threadx_stm32_data_ready_semaphore, "Data Ready", 0); tx_semaphore_create(&_z_threadx_stm32_data_processing_semaphore, "Data Processing", 1); _z_threadx_stm32_initialized = true; } _z_threadx_stm32_delimiter_offset = 0; _z_threadx_stm32_last_dma_offset = 0; _z_threadx_stm32_frame_len = 0; _z_threadx_stm32_frame_offset = 0; memset(_z_threadx_stm32_dma_buffer, 0, sizeof(_z_threadx_stm32_dma_buffer)); if (HAL_UARTEx_ReceiveToIdle_DMA(&ZENOH_HUART, _z_threadx_stm32_dma_buffer, RX_DMA_BUFFER_SIZE) != HAL_OK) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_threadx_stm32_uart_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_threadx_stm32_uart_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); return _z_threadx_stm32_uart_open_impl(); } static z_result_t _z_threadx_stm32_uart_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_threadx_stm32_uart_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_threadx_stm32_uart_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(dev); _ZP_UNUSED(baudrate); return _z_threadx_stm32_uart_open_impl(); } static void _z_threadx_stm32_uart_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } static size_t _z_threadx_stm32_uart_fill_frame(void) { size_t rb = 0; if (tx_semaphore_get(&_z_threadx_stm32_data_ready_semaphore, TX_TIMER_TICKS_PER_SECOND) != TX_SUCCESS) { return SIZE_MAX; } if (_z_threadx_stm32_delimiter_offset < _z_threadx_stm32_last_dma_offset) { rb = (RX_DMA_BUFFER_SIZE - _z_threadx_stm32_last_dma_offset) + _z_threadx_stm32_delimiter_offset; } else { rb = _z_threadx_stm32_delimiter_offset - _z_threadx_stm32_last_dma_offset; } if (rb == 0 || rb > sizeof(_z_threadx_stm32_frame_buffer)) { return SIZE_MAX; } if (_z_threadx_stm32_delimiter_offset < _z_threadx_stm32_last_dma_offset) { size_t second_part = RX_DMA_BUFFER_SIZE - _z_threadx_stm32_last_dma_offset; // flawfinder: ignore memcpy(_z_threadx_stm32_frame_buffer, _z_threadx_stm32_dma_buffer + _z_threadx_stm32_last_dma_offset, second_part); // flawfinder: ignore memcpy(_z_threadx_stm32_frame_buffer + second_part, _z_threadx_stm32_dma_buffer, _z_threadx_stm32_delimiter_offset); } else { // flawfinder: ignore memcpy(_z_threadx_stm32_frame_buffer, _z_threadx_stm32_dma_buffer + _z_threadx_stm32_last_dma_offset, rb); } _z_threadx_stm32_last_dma_offset = _z_threadx_stm32_delimiter_offset; tx_semaphore_put(&_z_threadx_stm32_data_processing_semaphore); _z_threadx_stm32_frame_len = rb; _z_threadx_stm32_frame_offset = 0; return rb; } static size_t _z_threadx_stm32_uart_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { _ZP_UNUSED(sock); if (_z_threadx_stm32_frame_offset == _z_threadx_stm32_frame_len) { if (_z_threadx_stm32_uart_fill_frame() == SIZE_MAX) { return SIZE_MAX; } } size_t available = _z_threadx_stm32_frame_len - _z_threadx_stm32_frame_offset; size_t chunk = len < available ? len : available; // flawfinder: ignore memcpy(ptr, &_z_threadx_stm32_frame_buffer[_z_threadx_stm32_frame_offset], chunk); _z_threadx_stm32_frame_offset += chunk; if (_z_threadx_stm32_frame_offset == _z_threadx_stm32_frame_len) { _z_threadx_stm32_frame_len = 0; _z_threadx_stm32_frame_offset = 0; } return chunk; } static size_t _z_threadx_stm32_uart_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { _ZP_UNUSED(sock); if (HAL_UART_Transmit(&ZENOH_HUART, (uint8_t *)ptr, len, 2000) != HAL_OK) { _Z_ERROR("Could not send to serial device!"); return SIZE_MAX; } return len; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_threadx_stm32_uart_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_threadx_stm32_uart_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_threadx_stm32_uart_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_threadx_stm32_uart_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_threadx_stm32_uart_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_threadx_stm32_uart_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_threadx_stm32_uart_write(sock, ptr, len); } void zptxstm32_rx_event_cb(UART_HandleTypeDef *huart, uint16_t offset) { static uint16_t last_offset = 0; if (huart != &ZENOH_HUART) { return; } if (offset == last_offset) { return; } if (offset < last_offset) { last_offset = 0; } while (last_offset < offset) { if (_z_threadx_stm32_dma_buffer[last_offset] == (uint8_t)0x00) { tx_semaphore_get(&_z_threadx_stm32_data_processing_semaphore, TX_WAIT_FOREVER); _z_threadx_stm32_delimiter_offset = last_offset + 1; tx_semaphore_put(&_z_threadx_stm32_data_ready_semaphore); } ++last_offset; } } void zptxstm32_error_event_cb(UART_HandleTypeDef *huart) { if (huart != &ZENOH_HUART) { return; } _Z_ERROR("UART error!"); HAL_UARTEx_ReceiveToIdle_DMA(&ZENOH_HUART, _z_threadx_stm32_dma_buffer, RX_DMA_BUFFER_SIZE); } #if ZENOH_THREADX_STM32_GEN_IRQ == 1 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t offset) { zptxstm32_rx_event_cb(huart, offset); } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { HAL_UART_DMAStop(huart); HAL_UART_Abort(huart); __HAL_UART_CLEAR_IDLEFLAG(huart); __HAL_UART_CLEAR_OREFLAG(huart); __HAL_UART_CLEAR_PEFLAG(huart); __HAL_UART_CLEAR_FEFLAG(huart); zptxstm32_error_event_cb(huart); } #endif #endif /* Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_THREADX_STM32) */ ================================================ FILE: src/link/transport/serial/uart_zephyr.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial.h" #if Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_ZEPHYR) #include #if KERNEL_VERSION_MAJOR == 2 #include #else #include #endif #include "zenoh-pico/utils/logging.h" static z_result_t _z_zephyr_uart_open_impl(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { if (sock == NULL || dev == NULL || baudrate == 0) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } sock->_serial = device_get_binding(dev); if (sock->_serial == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } const struct uart_config config = { .baudrate = baudrate, .parity = UART_CFG_PARITY_NONE, .stop_bits = UART_CFG_STOP_BITS_1, .data_bits = UART_CFG_DATA_BITS_8, .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, }; if (uart_configure(sock->_serial, &config) != 0) { sock->_serial = NULL; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_zephyr_uart_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { _ZP_UNUSED(sock); _ZP_UNUSED(txpin); _ZP_UNUSED(rxpin); _ZP_UNUSED(baudrate); // @TODO: To be implemented _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_zephyr_uart_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_zephyr_uart_open_impl(sock, dev, baudrate); } static z_result_t _z_zephyr_uart_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_zephyr_uart_open_from_pins(sock, txpin, rxpin, baudrate); } static z_result_t _z_zephyr_uart_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_zephyr_uart_open_impl(sock, dev, baudrate); } static void _z_zephyr_uart_close(_z_sys_net_socket_t *sock) { if (sock != NULL) { sock->_serial = NULL; } } static size_t _z_zephyr_uart_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { for (size_t i = 0; i < len; i++) { int res = -1; while (res != 0) { res = uart_poll_in(sock._serial, &ptr[i]); } } return len; } static size_t _z_zephyr_uart_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { for (size_t i = 0; i < len; i++) { uart_poll_out(sock._serial, ptr[i]); } return len; } z_result_t _z_serial_open_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_zephyr_uart_open_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_open_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_zephyr_uart_open_from_dev(sock, dev, baudrate); } z_result_t _z_serial_listen_from_pins(_z_sys_net_socket_t *sock, uint32_t txpin, uint32_t rxpin, uint32_t baudrate) { return _z_zephyr_uart_listen_from_pins(sock, txpin, rxpin, baudrate); } z_result_t _z_serial_listen_from_dev(_z_sys_net_socket_t *sock, const char *dev, uint32_t baudrate) { return _z_zephyr_uart_listen_from_dev(sock, dev, baudrate); } void _z_serial_close(_z_sys_net_socket_t *sock) { _z_zephyr_uart_close(sock); } size_t _z_serial_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_zephyr_uart_read(sock, ptr, len); } size_t _z_serial_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_zephyr_uart_write(sock, ptr, len); } #endif /* Z_FEATURE_LINK_SERIAL == 1 && defined(ZENOH_ZEPHYR) */ ================================================ FILE: src/link/transport/tcp/address.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/tcp.h" char *_z_tcp_address_parse_host(const _z_string_t *address) { return _z_endpoint_parse_host(address); } z_result_t _z_tcp_address_valid(const _z_string_t *address) { char *host = _z_tcp_address_parse_host(address); char *port = _z_endpoint_parse_port(address); z_result_t ret = ((host != NULL) && (port != NULL)) ? _Z_RES_OK : _Z_ERR_CONFIG_LOCATOR_INVALID; z_free(host); z_free(port); return ret; } z_result_t _z_tcp_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { z_result_t ret = _Z_RES_OK; char *host = _z_tcp_address_parse_host(address); char *port = _z_endpoint_parse_port(address); if ((host == NULL) || (port == NULL)) { ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } else { ret = _z_tcp_endpoint_init(ep, host, port); } z_free(host); z_free(port); return ret; } ================================================ FILE: src/link/transport/tcp/tcp_esp32.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_ESP32) #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_esp32_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_tcp_esp32_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } freeaddrinfo(ep->_iptcp); ep->_iptcp = NULL; } static z_result_t _z_tcp_esp32_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(tout); z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif #if LWIP_SO_LINGER == 1 struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(sock->_fd, it->ai_addr, it->ai_addrlen) < 0)) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } else { break; } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_esp32_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; return ret; } for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if (bind(sock->_fd, it->ai_addr, it->ai_addrlen) < 0) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } if (listen(sock->_fd, Z_LISTEN_MAX_CONNECTION_NB) < 0) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } return ret; } static z_result_t _z_tcp_esp32_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct sockaddr naddr; socklen_t nlen = sizeof(naddr); sock_out->_fd = -1; int con_socket = accept(sock_in->_fd, &naddr, &nlen); if (con_socket < 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int optflag = 1; if (setsockopt(con_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&optflag, sizeof(optflag)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 if (setsockopt(con_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif #if LWIP_SO_LINGER == 1 struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if (setsockopt(con_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif sock_out->_fd = con_socket; return _Z_RES_OK; } static void _z_tcp_esp32_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { shutdown(sock->_fd, SHUT_RDWR); close(sock->_fd); sock->_fd = -1; } } static size_t _z_tcp_esp32_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { ssize_t rb = recv(sock._fd, ptr, len, 0); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_esp32_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_esp32_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_esp32_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { ssize_t wb = send(sock._fd, ptr, len, 0); if (wb < (ssize_t)0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_esp32_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_esp32_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_esp32_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_esp32_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_esp32_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_esp32_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_esp32_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_esp32_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_esp32_write(sock, ptr, len); } #endif /* defined(ZP_PLATFORM_SOCKET_ESP32) */ ================================================ FILE: src/link/transport/tcp/tcp_freertos_plus_tcp.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP) #include #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_freertos_plus_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; if (FreeRTOS_getaddrinfo(s_address, NULL, NULL, &ep->_iptcp) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; return ret; } ep->_iptcp->ai_addr->sin_family = ep->_iptcp->ai_family; uint32_t port = strtoul(s_port, NULL, 10); if ((port > (uint32_t)0) && (port <= (uint32_t)65535)) { ep->_iptcp->ai_addr->sin_port = FreeRTOS_htons((uint16_t)port); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_tcp_freertos_plus_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } FreeRTOS_freeaddrinfo(ep->_iptcp); ep->_iptcp = NULL; } static z_result_t _z_tcp_freertos_plus_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_socket = FreeRTOS_socket(endpoint._iptcp->ai_family, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP); if (sock->_socket != FREERTOS_INVALID_SOCKET) { TickType_t receive_timeout = pdMS_TO_TICKS(tout); if (FreeRTOS_setsockopt(sock->_socket, 0, FREERTOS_SO_RCVTIMEO, &receive_timeout, 0) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } else if (FreeRTOS_connect(sock->_socket, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_freertos_plus_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { sock->_socket = FreeRTOS_socket(endpoint._iptcp->ai_family, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP); if (sock->_socket == FREERTOS_INVALID_SOCKET) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (FreeRTOS_bind(sock->_socket, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen) != 0) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (FreeRTOS_listen(sock->_socket, Z_LISTEN_MAX_CONNECTION_NB) != 0) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_tcp_freertos_plus_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct freertos_sockaddr naddr; socklen_t nlen = sizeof(naddr); sock_out->_socket = FREERTOS_INVALID_SOCKET; Socket_t con_socket = FreeRTOS_accept(sock_in->_socket, &naddr, &nlen); if (con_socket == FREERTOS_INVALID_SOCKET) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } TickType_t receive_timeout = pdMS_TO_TICKS(Z_CONFIG_SOCKET_TIMEOUT); if (FreeRTOS_setsockopt(con_socket, 0, FREERTOS_SO_RCVTIMEO, &receive_timeout, 0) != 0) { FreeRTOS_closesocket(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock_out->_socket = con_socket; return _Z_RES_OK; } static void _z_tcp_freertos_plus_tcp_close(_z_sys_net_socket_t *sock) { if (sock->_socket != FREERTOS_INVALID_SOCKET) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; } } static size_t _z_tcp_freertos_plus_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { BaseType_t rb = FreeRTOS_recv(sock._socket, ptr, len, 0); if (rb < 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_freertos_plus_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_freertos_plus_tcp_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_freertos_plus_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return (size_t)FreeRTOS_send(sock._socket, ptr, len, 0); } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_freertos_plus_tcp_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_freertos_plus_tcp_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_freertos_plus_tcp_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_freertos_plus_tcp_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_freertos_plus_tcp_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_freertos_plus_tcp_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_freertos_plus_tcp_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_freertos_plus_tcp_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_freertos_plus_tcp_write(sock, ptr, len); } #endif /* defined(ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP) */ ================================================ FILE: src/link/transport/tcp/tcp_lwip.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_LWIP) && (Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1 || Z_FEATURE_LINK_WS == 1) #include #include #include #include "lwip/netdb.h" #include "lwip/sockets.h" #include "zenoh-pico/link/transport/lwip_socket.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_lwip_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } else if (ep->_iptcp != NULL && ep->_iptcp->ai_addr != NULL) { ep->_iptcp->ai_addr->sa_family = ep->_iptcp->ai_family; } return ret; } static void _z_tcp_lwip_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } freeaddrinfo(ep->_iptcp); ep->_iptcp = NULL; } static z_result_t _z_tcp_lwip_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; _z_lwip_socket_set(sock, socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol)); if (_z_lwip_socket_get(*sock) != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(_z_lwip_socket_get(*sock), it->ai_addr, it->ai_addrlen) < 0)) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } else { break; } } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_lwip_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; _z_lwip_socket_set(sock, socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol)); if (_z_lwip_socket_get(*sock) == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); return ret; } for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if (bind(_z_lwip_socket_get(*sock), it->ai_addr, it->ai_addrlen) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } if (listen(_z_lwip_socket_get(*sock), Z_LISTEN_MAX_CONNECTION_NB) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } return ret; } static z_result_t _z_tcp_lwip_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct sockaddr naddr; socklen_t nlen = sizeof(naddr); _z_lwip_socket_set(sock_out, -1); int con_socket = lwip_accept(_z_lwip_socket_get(*sock_in), &naddr, &nlen); if (con_socket < 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_time_t tv; tv.tv_sec = Z_CONFIG_SOCKET_TIMEOUT / (uint32_t)1000; tv.tv_usec = (Z_CONFIG_SOCKET_TIMEOUT % (uint32_t)1000) * (uint32_t)1000; if (setsockopt(con_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0) { lwip_close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int flags = 1; if (setsockopt(con_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) { lwip_close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 if (setsockopt(con_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0) { lwip_close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if (setsockopt(con_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0) { lwip_close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_lwip_socket_set(sock_out, con_socket); return _Z_RES_OK; } static void _z_tcp_lwip_close(_z_sys_net_socket_t *sock) { if (_z_lwip_socket_get(*sock) >= 0) { shutdown(_z_lwip_socket_get(*sock), SHUT_RDWR); close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } } static size_t _z_tcp_lwip_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { ssize_t rb = recv(_z_lwip_socket_get(sock), ptr, len, 0); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_lwip_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_lwip_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_lwip_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return (size_t)send(_z_lwip_socket_get(sock), ptr, len, 0); } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_lwip_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_lwip_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_lwip_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_lwip_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_lwip_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_lwip_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_lwip_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_lwip_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_lwip_write(sock, ptr, len); } #endif /* defined(ZP_PLATFORM_SOCKET_LWIP) */ ================================================ FILE: src/link/transport/tcp/tcp_mbed.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_MBED) #include #include extern "C" { #include #include #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_mbed_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { uint32_t port = strtoul(s_port, NULL, 10); if ((port == 0U) || (port > 65535U)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } ep->_iptcp = new SocketAddress(s_address, port); if (ep->_iptcp == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static void _z_tcp_mbed_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } delete ep->_iptcp; ep->_iptcp = NULL; } static z_result_t _z_tcp_mbed_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { NetworkInterface *iface = NetworkInterface::get_default_instance(); if (iface == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_tcp = new TCPSocket(); if (sock->_tcp == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_tcp->set_timeout((int)tout); // flawfinder: ignore if (sock->_tcp->open(iface) < 0) { delete sock->_tcp; sock->_tcp = NULL; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (sock->_tcp->connect(*endpoint._iptcp) < 0) { sock->_tcp->close(); delete sock->_tcp; sock->_tcp = NULL; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_tcp_mbed_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static z_result_t _z_tcp_mbed_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { _ZP_UNUSED(sock_in); _ZP_UNUSED(sock_out); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static void _z_tcp_mbed_close(_z_sys_net_socket_t *sock) { if (sock->_tcp != NULL) { sock->_tcp->close(); delete sock->_tcp; sock->_tcp = NULL; } } static size_t _z_tcp_mbed_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { nsapi_size_or_error_t rb = sock._tcp->recv(ptr, len); if (rb < 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_mbed_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_mbed_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_mbed_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { nsapi_size_or_error_t wb = sock._tcp->send(ptr, len); if (wb < 0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_mbed_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_mbed_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_mbed_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_mbed_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_mbed_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_mbed_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_mbed_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_mbed_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_mbed_write(sock, ptr, len); } } // extern "C" #endif /* defined(ZP_PLATFORM_SOCKET_MBED) */ ================================================ FILE: src/link/transport/tcp/tcp_opencr.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_OPENCR) #include #include #include #include extern "C" { #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_opencr_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; ep->_iptcp._addr = new IPAddress(); if (!ep->_iptcp._addr->fromString(s_address)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } ep->_iptcp._port = strtoul(s_port, NULL, 10); if ((ep->_iptcp._port < (uint32_t)1) || (ep->_iptcp._port > (uint32_t)65535)) { // Port numbers should range from 1 to 65535 _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete ep->_iptcp._addr; ep->_iptcp._addr = NULL; } return ret; } static void _z_tcp_opencr_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp._addr == NULL)) { return; } delete ep->_iptcp._addr; ep->_iptcp._addr = NULL; } static z_result_t _z_tcp_opencr_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(tout); z_result_t ret = _Z_RES_OK; sock->_tcp = new WiFiClient(); if (!sock->_tcp->connect(*endpoint._iptcp._addr, endpoint._iptcp._port)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete sock->_tcp; sock->_tcp = NULL; } return ret; } static z_result_t _z_tcp_opencr_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static z_result_t _z_tcp_opencr_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { _ZP_UNUSED(sock_in); _ZP_UNUSED(sock_out); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_tcp_opencr_close(_z_sys_net_socket_t *sock) { if (sock->_tcp != NULL) { sock->_tcp->stop(); delete sock->_tcp; sock->_tcp = NULL; } } static size_t _z_tcp_opencr_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { if (sock._tcp->available() > 0) { // flawfinder: ignore int rb = sock._tcp->read(ptr, len); if (rb < 0) { return SIZE_MAX; } return (size_t)rb; } return 0; } static size_t _z_tcp_opencr_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_opencr_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_opencr_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { sock._tcp->write(ptr, len); return len; } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_opencr_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_opencr_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_opencr_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_opencr_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_opencr_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_opencr_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_opencr_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_opencr_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_opencr_write(sock, ptr, len); } } // extern "C" #endif /* defined(ZP_PLATFORM_SOCKET_OPENCR) */ ================================================ FILE: src/link/transport/tcp/tcp_posix.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_POSIX) #include #include #include #include #include #include #include #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_posix_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_tcp_posix_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } freeaddrinfo(ep->_iptcp); ep->_iptcp = NULL; } static z_result_t _z_tcp_posix_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = (time_t)(tout / (uint32_t)1000); tv.tv_usec = (suseconds_t)((tout % (uint32_t)1000) * (uint32_t)1000); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if defined(ZENOH_MACOS) || defined(ZENOH_BSD) int nosigpipe_val = 1; setsockopt(sock->_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&nosigpipe_val, sizeof(int)); #endif for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(sock->_fd, it->ai_addr, it->ai_addrlen) < 0)) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } else { break; } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_posix_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int value = true; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if defined(ZENOH_MACOS) || defined(ZENOH_BSD) int nosigpipe_val = 1; setsockopt(sock->_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&nosigpipe_val, sizeof(int)); #endif if (ret != _Z_RES_OK) { close(sock->_fd); return ret; } int addr_count = 0; for (struct addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { addr_count++; char addr_str[INET6_ADDRSTRLEN]; const char *family_str = (it->ai_family == AF_INET) ? "IPv4" : (it->ai_family == AF_INET6) ? "IPv6" : "Unknown"; if (it->ai_family == AF_INET) { inet_ntop(AF_INET, &((struct sockaddr_in *)it->ai_addr)->sin_addr, addr_str, INET_ADDRSTRLEN); } else if (it->ai_family == AF_INET6) { inet_ntop(AF_INET6, &((struct sockaddr_in6 *)it->ai_addr)->sin6_addr, addr_str, INET6_ADDRSTRLEN); } else { snprintf(addr_str, sizeof(addr_str), "%s", "unknown"); } _Z_DEBUG("Trying address %d: %s (%s), family=%d", addr_count, addr_str, family_str, it->ai_family); if (bind(sock->_fd, it->ai_addr, it->ai_addrlen) < 0) { _Z_DEBUG("bind() failed for address %s: %s", addr_str, strerror(errno)); if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } continue; } _Z_DEBUG("bind() successful for address %s", addr_str); if (listen(sock->_fd, Z_LISTEN_MAX_CONNECTION_NB) < 0) { _Z_DEBUG("listen() failed for address %s: %s", addr_str, strerror(errno)); if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } continue; } _Z_DEBUG("listen() successful for address %s", addr_str); break; } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } return ret; } static z_result_t _z_tcp_posix_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct sockaddr naddr; unsigned int nlen = sizeof(naddr); sock_out->_fd = -1; int con_socket = accept(sock_in->_fd, &naddr, &nlen); if (con_socket < 0) { if (errno == EBADF) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } } z_time_t tv; tv.tv_sec = Z_CONFIG_SOCKET_TIMEOUT / (uint32_t)1000; tv.tv_usec = (Z_CONFIG_SOCKET_TIMEOUT % (uint32_t)1000) * (uint32_t)1000; if (setsockopt(con_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int flags = 1; if (setsockopt(con_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 if (setsockopt(con_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if (setsockopt(con_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock_out->_fd = con_socket; return _Z_RES_OK; } static void _z_tcp_posix_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { shutdown(sock->_fd, SHUT_RDWR); close(sock->_fd); sock->_fd = -1; } } static size_t _z_tcp_posix_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { ssize_t rb = recv(sock._fd, ptr, len, 0); if (rb < (ssize_t)0) { if (errno != EAGAIN) { _Z_DEBUG("Errno: %d\n", errno); } return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_posix_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_posix_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n += rb; pos = _z_ptr_u8_offset(pos, (ptrdiff_t)rb); } while (n != len); return n; } static size_t _z_tcp_posix_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { #if defined(ZENOH_LINUX) return (size_t)send(sock._fd, ptr, len, MSG_NOSIGNAL); #else return (size_t)send(sock._fd, ptr, len, 0); #endif } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_posix_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_posix_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_posix_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_posix_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_posix_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_posix_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_posix_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_posix_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_posix_write(sock, ptr, len); } #endif /* defined(ZP_PLATFORM_SOCKET_POSIX) */ ================================================ FILE: src/link/transport/tcp/tcp_windows.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_WINDOWS) #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static WSADATA _z_tcp_windows_wsa_data; static z_result_t _z_tcp_windows_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; ep->_ep._iptcp = NULL; if (WSAStartup(MAKEWORD(2, 2), &_z_tcp_windows_wsa_data) == 0) { ADDRINFOA hints; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_address, s_port, &hints, &ep->_ep._iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); WSACleanup(); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_tcp_windows_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_ep._iptcp == NULL)) { return; } freeaddrinfo(ep->_ep._iptcp); ep->_ep._iptcp = NULL; WSACleanup(); } static z_result_t _z_tcp_windows_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_tcp_windows_wsa_data) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } sock->_sock._fd = socket(endpoint._ep._iptcp->ai_family, endpoint._ep._iptcp->ai_socktype, endpoint._ep._iptcp->ai_protocol); if (sock->_sock._fd != INVALID_SOCKET) { DWORD tv = tout; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_LINGER, (const char *)&ling, sizeof(ling)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } ADDRINFOA *it = NULL; for (it = endpoint._ep._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(sock->_sock._fd, it->ai_addr, (int)it->ai_addrlen) < 0)) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } else { break; } } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_windows_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_tcp_windows_wsa_data) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_sock._fd = socket(endpoint._ep._iptcp->ai_family, endpoint._ep._iptcp->ai_socktype, endpoint._ep._iptcp->ai_protocol); if (sock->_sock._fd == INVALID_SOCKET) { WSACleanup(); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int value = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&value, sizeof(value)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int flags = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_KEEPALIVE, (const char *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if Z_FEATURE_TCP_NODELAY == 1 if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_LINGER, (const char *)&ling, sizeof(ling)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); WSACleanup(); return ret; } ADDRINFOA *it = NULL; for (it = endpoint._ep._iptcp; it != NULL; it = it->ai_next) { if (bind(sock->_sock._fd, it->ai_addr, (int)it->ai_addrlen) < 0) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } if (listen(sock->_sock._fd, Z_LISTEN_MAX_CONNECTION_NB) < 0) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; } WSACleanup(); return ret; } static z_result_t _z_tcp_windows_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct sockaddr naddr; int nlen = sizeof(naddr); sock_out->_sock._fd = INVALID_SOCKET; SOCKET con_socket = accept(sock_in->_sock._fd, &naddr, &nlen); if (con_socket == INVALID_SOCKET) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } DWORD tv = Z_CONFIG_SOCKET_TIMEOUT; if (setsockopt(con_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0) { closesocket(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } int flags = 1; if (setsockopt(con_socket, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) < 0) { closesocket(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 if (setsockopt(con_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) < 0) { closesocket(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if (setsockopt(con_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0) { closesocket(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock_out->_sock._fd = con_socket; return _Z_RES_OK; } static void _z_tcp_windows_close(_z_sys_net_socket_t *sock) { if (sock->_sock._fd != INVALID_SOCKET) { shutdown(sock->_sock._fd, SD_BOTH); closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); } } static size_t _z_tcp_windows_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { int rb = recv(sock._sock._fd, (char *)ptr, (int)len, 0); if (rb == SOCKET_ERROR) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_windows_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_windows_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_windows_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { int wb = send(sock._sock._fd, (const char *)ptr, (int)len, 0); if (wb == SOCKET_ERROR) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_windows_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_windows_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_windows_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_windows_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_windows_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_windows_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_windows_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_windows_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_windows_write(sock, ptr, len); } #endif /* defined(ZP_PLATFORM_SOCKET_WINDOWS) */ ================================================ FILE: src/link/transport/tcp/tcp_zephyr.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #if defined(ZP_PLATFORM_SOCKET_ZEPHYR) && (Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1 || Z_FEATURE_LINK_WS == 1) #include #include #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_tcp_zephyr_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct zsock_addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_tcp_zephyr_endpoint_clear(_z_sys_net_endpoint_t *ep) { if ((ep == NULL) || (ep->_iptcp == NULL)) { return; } freeaddrinfo(ep->_iptcp); ep->_iptcp = NULL; } static z_result_t _z_tcp_zephyr_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { /* Zephyr may reject this option depending on the network stack configuration. */ _Z_ERROR_LOG(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif #if LWIP_SO_LINGER == 1 struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif for (struct zsock_addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(sock->_fd, it->ai_addr, it->ai_addrlen) < 0)) { if (it->ai_next == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } } else { break; } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_tcp_zephyr_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 int optflag = 1; if (setsockopt(sock->_fd, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); close(sock->_fd); sock->_fd = -1; return _Z_ERR_GENERIC; } #endif for (struct zsock_addrinfo *it = endpoint._iptcp; it != NULL; it = it->ai_next) { if (bind(sock->_fd, it->ai_addr, it->ai_addrlen) < 0) { if (it->ai_next != NULL) { continue; } _Z_ERROR_LOG(_Z_ERR_GENERIC); break; } if (listen(sock->_fd, Z_LISTEN_MAX_CONNECTION_NB) < 0) { if (it->ai_next != NULL) { continue; } _Z_ERROR_LOG(_Z_ERR_GENERIC); break; } return _Z_RES_OK; } close(sock->_fd); sock->_fd = -1; return _Z_ERR_GENERIC; } static z_result_t _z_tcp_zephyr_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { struct sockaddr naddr; unsigned int nlen = sizeof(naddr); sock_out->_fd = -1; int con_socket = accept(sock_in->_fd, &naddr, &nlen); if (con_socket < 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_TCP_NODELAY == 1 int optflag = 1; if (setsockopt(con_socket, IPPROTO_TCP, TCP_NODELAY, (void *)&optflag, sizeof(optflag)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif #if LWIP_SO_LINGER == 1 struct linger ling; ling.l_onoff = 1; ling.l_linger = Z_TRANSPORT_LEASE / 1000; if (setsockopt(con_socket, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(struct linger)) < 0) { close(con_socket); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif sock_out->_fd = con_socket; return _Z_RES_OK; } static void _z_tcp_zephyr_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } static size_t _z_tcp_zephyr_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { ssize_t rb = recv(sock._fd, ptr, len, 0); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_tcp_zephyr_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_tcp_zephyr_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_tcp_zephyr_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return (size_t)send(sock._fd, ptr, len, 0); } z_result_t _z_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_tcp_zephyr_endpoint_init(ep, address, port); } void _z_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_zephyr_endpoint_clear(ep); } z_result_t _z_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_tcp_zephyr_open(sock, endpoint, tout); } z_result_t _z_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint) { return _z_tcp_zephyr_listen(sock, endpoint); } z_result_t _z_tcp_accept(const _z_sys_net_socket_t *sock_in, _z_sys_net_socket_t *sock_out) { return _z_tcp_zephyr_accept(sock_in, sock_out); } void _z_tcp_close(_z_sys_net_socket_t *sock) { _z_tcp_zephyr_close(sock); } size_t _z_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_zephyr_read(sock, ptr, len); } size_t _z_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_tcp_zephyr_read_exact(sock, ptr, len); } size_t _z_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_tcp_zephyr_write(sock, ptr, len); } #endif ================================================ FILE: src/link/transport/udp/address.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/udp_unicast.h" z_result_t _z_udp_unicast_address_valid(const _z_string_t *address) { char *host = _z_endpoint_parse_host(address); char *port = _z_endpoint_parse_port(address); z_result_t ret = ((host != NULL) && (port != NULL)) ? _Z_RES_OK : _Z_ERR_CONFIG_LOCATOR_INVALID; z_free(host); z_free(port); return ret; } z_result_t _z_udp_unicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { z_result_t ret = _Z_RES_OK; char *host = _z_endpoint_parse_host(address); char *port = _z_endpoint_parse_port(address); if ((host == NULL) || (port == NULL)) { ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } else { ret = _z_udp_unicast_endpoint_init(ep, host, port); } z_free(host); z_free(port); return ret; } ================================================ FILE: src/link/transport/udp/raweth_unix.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/raweth.h" #if Z_FEATURE_RAWETH_TRANSPORT == 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/system/platform/unix.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #if !defined(__linux) #error "Raweth transport only supported on linux systems" #else #include void _z_raweth_clear_mapping_entry(_zp_raweth_mapping_entry_t *entry) { _z_string_clear(&entry->_keyexpr); } z_result_t _z_open_raweth(_z_sys_net_socket_t *sock, const char *interface) { z_result_t ret = _Z_RES_OK; // Open a raw network socket in promiscuous mode sock->_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (sock->_fd == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Get the index of the interface to send on struct ifreq if_idx; memset(&if_idx, 0, sizeof(struct ifreq)); strncpy(if_idx.ifr_name, interface, strlen(interface)); if (ioctl(sock->_fd, SIOCGIFINDEX, &if_idx) < 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Bind the socket struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); addr.sll_ifindex = if_idx.ifr_ifindex; addr.sll_pkttype = PACKET_HOST | PACKET_BROADCAST | PACKET_MULTICAST; if (bind(sock->_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { close(sock->_fd); _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_close_raweth(_z_sys_net_socket_t *sock) { z_result_t ret = _Z_RES_OK; if (close(sock->_fd) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } size_t _z_send_raweth(const _z_sys_net_socket_t *sock, const void *buff, size_t buff_len) { // Send data ssize_t wb = write(sock->_fd, buff, buff_len); if (wb < 0) { return SIZE_MAX; } return (size_t)wb; } size_t _z_receive_raweth(const _z_sys_net_socket_t *sock, void *buff, size_t buff_len, _z_slice_t *addr, const _zp_raweth_whitelist_array_t *whitelist) { // Read from socket ssize_t bytesRead = recvfrom(sock->_fd, buff, buff_len, 0, NULL, NULL); if ((bytesRead <= 0) || (bytesRead < (ssize_t)sizeof(_zp_eth_header_t))) { return SIZE_MAX; } bool is_valid = true; // Address filtering (only if there is a whitelist) if (_zp_raweth_whitelist_array_len(whitelist) > 0) { is_valid = false; const _zp_eth_header_t *header = (_zp_eth_header_t *)buff; for (size_t i = 0; i < _zp_raweth_whitelist_array_len(whitelist); i++) { const _zp_raweth_whitelist_entry_t *entry = _zp_raweth_whitelist_array_get(whitelist, i); if (memcmp(&header->smac, entry->_mac, _ZP_MAC_ADDR_LENGTH) == 0) { is_valid = true; break; } } } // Ignore packet from unknown sources if (!is_valid) { return SIZE_MAX; } // Copy sender mac if needed if (addr != NULL) { uint8_t *header_addr = (uint8_t *)buff; addr->len = sizeof(ETH_ALEN); (void)memcpy((uint8_t *)addr->start, (header_addr + ETH_ALEN), sizeof(ETH_ALEN)); } return (size_t)bytesRead; } uint16_t _z_raweth_ntohs(uint16_t val) { return ntohs(val); } uint16_t _z_raweth_htons(uint16_t val) { return htons(val); } #endif // defined(__linux) #endif // Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/link/transport/udp/udp_esp32.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_ESP32) #include #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_esp32_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_UDP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_esp32_endpoint_clear(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_iptcp); } static z_result_t _z_udp_esp32_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_esp32_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_esp32_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } static size_t _z_udp_esp32_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { struct sockaddr_storage raddr; socklen_t addrlen = sizeof(struct sockaddr_storage); ssize_t rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &addrlen); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_esp32_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_esp32_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_esp32_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { ssize_t wb = sendto(sock._fd, ptr, len, 0, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen); if (wb < (ssize_t)0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_esp32_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_esp32_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_esp32_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_esp32_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_esp32_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_esp32_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_esp32_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_esp32_write(sock, ptr, len, endpoint); } #endif /* defined(ZP_PLATFORM_SOCKET_ESP32) */ ================================================ FILE: src/link/transport/udp/udp_freertos_plus_tcp.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP) #include #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_freertos_plus_tcp_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; if (FreeRTOS_getaddrinfo(s_address, NULL, NULL, &ep->_iptcp) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; return ret; } ep->_iptcp->ai_addr->sin_family = ep->_iptcp->ai_family; uint32_t port = strtoul(s_port, NULL, 10); if ((port > (uint32_t)0) && (port <= (uint32_t)65535)) { ep->_iptcp->ai_addr->sin_port = FreeRTOS_htons((uint16_t)port); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_freertos_plus_tcp_endpoint_clear(_z_sys_net_endpoint_t *ep) { FreeRTOS_freeaddrinfo(ep->_iptcp); } static z_result_t _z_udp_freertos_plus_tcp_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_socket = FreeRTOS_socket(endpoint._iptcp->ai_family, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP); if (sock->_socket != FREERTOS_INVALID_SOCKET) { TickType_t receive_timeout = pdMS_TO_TICKS(tout); if (FreeRTOS_setsockopt(sock->_socket, 0, FREERTOS_SO_RCVTIMEO, &receive_timeout, 0) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_freertos_plus_tcp_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_socket = FreeRTOS_socket(endpoint._iptcp->ai_family, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP); if (sock->_socket != FREERTOS_INVALID_SOCKET) { TickType_t receive_timeout = pdMS_TO_TICKS(tout); if (FreeRTOS_setsockopt(sock->_socket, 0, FREERTOS_SO_RCVTIMEO, &receive_timeout, 0) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } else if (FreeRTOS_bind(sock->_socket, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_freertos_plus_tcp_close(_z_sys_net_socket_t *sock) { if (sock->_socket != FREERTOS_INVALID_SOCKET) { FreeRTOS_closesocket(sock->_socket); sock->_socket = FREERTOS_INVALID_SOCKET; } } static size_t _z_udp_freertos_plus_tcp_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { struct freertos_sockaddr raddr; uint32_t addrlen = sizeof(struct freertos_sockaddr); int32_t rb = FreeRTOS_recvfrom(sock._socket, ptr, len, 0, &raddr, &addrlen); if (rb < 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_freertos_plus_tcp_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_freertos_plus_tcp_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_freertos_plus_tcp_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return (size_t)FreeRTOS_sendto(sock._socket, ptr, len, 0, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen); } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_freertos_plus_tcp_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_freertos_plus_tcp_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_freertos_plus_tcp_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_freertos_plus_tcp_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_freertos_plus_tcp_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_freertos_plus_tcp_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_freertos_plus_tcp_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_freertos_plus_tcp_write(sock, ptr, len, endpoint); } #endif /* defined(ZP_PLATFORM_SOCKET_FREERTOS_PLUS_TCP) */ ================================================ FILE: src/link/transport/udp/udp_lwip.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_LWIP) && (Z_FEATURE_LINK_UDP_UNICAST == 1) #include #include #include #include "lwip/netdb.h" #include "lwip/sockets.h" #include "zenoh-pico/link/transport/lwip_socket.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_lwip_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_UDP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } else if (ep->_iptcp != NULL && ep->_iptcp->ai_addr != NULL) { ep->_iptcp->ai_addr->sa_family = ep->_iptcp->ai_family; } return ret; } static void _z_udp_lwip_endpoint_clear(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_iptcp); } static z_result_t _z_udp_lwip_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; _z_lwip_socket_set(sock, socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol)); if (_z_lwip_socket_get(*sock) != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_lwip_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_lwip_close(_z_sys_net_socket_t *sock) { if (_z_lwip_socket_get(*sock) >= 0) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } } static size_t _z_udp_lwip_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { struct sockaddr_storage raddr; unsigned int addrlen = sizeof(struct sockaddr_storage); ssize_t rb = recvfrom(_z_lwip_socket_get(sock), ptr, len, 0, (struct sockaddr *)&raddr, &addrlen); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_lwip_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_lwip_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, (ptrdiff_t)rb); } while (n != len); return n; } static size_t _z_udp_lwip_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return (size_t)sendto(_z_lwip_socket_get(sock), ptr, len, 0, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen); } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_lwip_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_lwip_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_lwip_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_lwip_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_lwip_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_lwip_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_lwip_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_lwip_write(sock, ptr, len, endpoint); } #endif /* defined(ZP_PLATFORM_SOCKET_LWIP) */ ================================================ FILE: src/link/transport/udp/udp_mbed.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_MBED) #include #include extern "C" { #include #include #include "zenoh-pico/link/transport/udp_unicast.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_mbed_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { uint32_t port = strtoul(s_port, NULL, 10); if ((port == 0U) || (port > 65535U)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } ep->_iptcp = new SocketAddress(s_address, port); if (ep->_iptcp == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static void _z_udp_mbed_endpoint_clear(_z_sys_net_endpoint_t *ep) { delete ep->_iptcp; ep->_iptcp = NULL; } static z_result_t _z_udp_mbed_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(endpoint); NetworkInterface *iface = NetworkInterface::get_default_instance(); if (iface == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_udp = new UDPSocket(); if (sock->_udp == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } sock->_udp->set_timeout((int)tout); // flawfinder: ignore if (sock->_udp->open(iface) < 0) { delete sock->_udp; sock->_udp = NULL; _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } static z_result_t _z_udp_mbed_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } static void _z_udp_mbed_close(_z_sys_net_socket_t *sock) { if (sock->_udp != NULL) { sock->_udp->close(); delete sock->_udp; sock->_udp = NULL; } } static size_t _z_udp_mbed_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { SocketAddress raddr; nsapi_size_or_error_t rb = sock._udp->recvfrom(&raddr, ptr, len); if (rb < 0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_mbed_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_mbed_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_mbed_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { nsapi_size_or_error_t wb = sock._udp->sendto(*endpoint._iptcp, ptr, len); if (wb < 0) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_mbed_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_mbed_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_mbed_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_mbed_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_mbed_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_mbed_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_mbed_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_mbed_write(sock, ptr, len, endpoint); } } // extern "C" #endif /* defined(ZP_PLATFORM_SOCKET_MBED) */ ================================================ FILE: src/link/transport/udp/udp_multicast_esp32.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZP_PLATFORM_SOCKET_ESP32) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static unsigned int _z_esp32_udp_multicast_ipv6_outbound_ifindex(void) { // TODO: route this through platform/socket support once esp32 multicast iface selection is explicit. return 0U; } static unsigned int _z_esp32_udp_multicast_ipv6_membership_ifindex(void) { // TODO: route this through platform/socket support once esp32 multicast iface selection is explicit. return 1U; } static z_result_t _z_esp32_udp_multicast_make_local_addr(int family, in_port_t port, struct sockaddr **lsockaddr, socklen_t *addrlen) { z_result_t ret = _Z_RES_OK; *lsockaddr = NULL; *addrlen = 0; if (family == AF_INET) { *lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in)); if (*lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(struct sockaddr_in)); *addrlen = sizeof(struct sockaddr_in); struct sockaddr_in *c_laddr = (struct sockaddr_in *)*lsockaddr; c_laddr->sin_family = AF_INET; c_laddr->sin_addr.s_addr = INADDR_ANY; c_laddr->sin_port = port; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (family == AF_INET6) { *lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in6)); if (*lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(struct sockaddr_in6)); *addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 *c_laddr = (struct sockaddr_in6 *)*lsockaddr; c_laddr->sin6_family = AF_INET6; c_laddr->sin6_addr = in6addr_any; c_laddr->sin6_port = port; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_esp32_udp_multicast_configure_outbound_iface(int fd, const struct sockaddr *lsockaddr) { z_result_t ret = _Z_RES_OK; if (lsockaddr->sa_family == AF_INET) { if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &((const struct sockaddr_in *)lsockaddr)->sin_addr, sizeof(struct in_addr)) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (lsockaddr->sa_family == AF_INET6) { unsigned int ifindex = _z_esp32_udp_multicast_ipv6_outbound_ifindex(); if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_esp32_udp_multicast_apply_membership(int fd, int family, int option, const void *addr_bytes) { z_result_t ret = _Z_RES_OK; if (family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.imr_multiaddr, addr_bytes, sizeof(struct in_addr)); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(fd, IPPROTO_IP, option, &mreq, sizeof(mreq)) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.ipv6mr_multiaddr, addr_bytes, sizeof(struct in6_addr)); mreq.ipv6mr_interface = _z_esp32_udp_multicast_ipv6_membership_ifindex(); if (setsockopt(fd, IPPROTO_IPV6, option, &mreq, sizeof(mreq)) < 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_esp32_udp_multicast_apply_primary_membership(int fd, const _z_sys_net_endpoint_t rep, int option) { if (rep._iptcp->ai_family == AF_INET) { return _z_esp32_udp_multicast_apply_membership(fd, rep._iptcp->ai_family, option, &((const struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr); } if (rep._iptcp->ai_family == AF_INET6) { return _z_esp32_udp_multicast_apply_membership(fd, rep._iptcp->ai_family, option, &((const struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr); } _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { _ZP_UNUSED(iface); z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; socklen_t addrlen = 0; ret = _z_esp32_udp_multicast_make_local_addr(rep._iptcp->ai_family, 0, &lsockaddr, &addrlen); if ((ret == _Z_RES_OK) && (addrlen != 0U)) { sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(sock->_fd, lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (getsockname(sock->_fd, lsockaddr, &addrlen) == -1)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret == _Z_RES_OK) { ret = _z_esp32_udp_multicast_configure_outbound_iface(sock->_fd, lsockaddr); } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return ret; } struct addrinfo *laddr = (struct addrinfo *)z_malloc(sizeof(struct addrinfo)); if (laddr == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return _Z_ERR_GENERIC; } laddr->ai_flags = 0; laddr->ai_family = rep._iptcp->ai_family; laddr->ai_socktype = rep._iptcp->ai_socktype; laddr->ai_protocol = rep._iptcp->ai_protocol; laddr->ai_addrlen = addrlen; laddr->ai_addr = lsockaddr; laddr->ai_canonname = NULL; laddr->ai_next = NULL; lep->_iptcp = laddr; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { z_free(lsockaddr); } } else if (ret == _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { _ZP_UNUSED(iface); (void)join; z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; socklen_t addrlen = 0; if (rep._iptcp->ai_family == AF_INET) { ret = _z_esp32_udp_multicast_make_local_addr( rep._iptcp->ai_family, ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_port, &lsockaddr, &addrlen); } else if (rep._iptcp->ai_family == AF_INET6) { ret = _z_esp32_udp_multicast_make_local_addr( rep._iptcp->ai_family, ((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_port, &lsockaddr, &addrlen); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (addrlen != 0U)) { sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(sock->_fd, lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret == _Z_RES_OK) { if (rep._iptcp->ai_family == AF_INET) { ret = _z_esp32_udp_multicast_apply_primary_membership(sock->_fd, rep, IP_ADD_MEMBERSHIP); } else if (rep._iptcp->ai_family == AF_INET6) { ret = _z_esp32_udp_multicast_apply_primary_membership(sock->_fd, rep, IPV6_JOIN_GROUP); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } z_free(lsockaddr); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _ZP_UNUSED(lep); if (sockrecv->_fd >= 0) { if (rep._iptcp->ai_family == AF_INET) { (void)_z_esp32_udp_multicast_apply_primary_membership(sockrecv->_fd, rep, IP_DROP_MEMBERSHIP); } else if (rep._iptcp->ai_family == AF_INET6) { (void)_z_esp32_udp_multicast_apply_primary_membership(sockrecv->_fd, rep, IPV6_LEAVE_GROUP); } } if (sockrecv->_fd >= 0) { close(sockrecv->_fd); sockrecv->_fd = -1; } if (socksend->_fd >= 0) { close(socksend->_fd); socksend->_fd = -1; } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { struct sockaddr_storage raddr; socklen_t raddrlen = sizeof(struct sockaddr_storage); ssize_t rb = 0; do { rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &raddrlen); if (rb < (ssize_t)0) { rb = SIZE_MAX; break; } if (lep._iptcp->ai_family == AF_INET) { struct sockaddr_in *a = ((struct sockaddr_in *)lep._iptcp->ai_addr); struct sockaddr_in *b = ((struct sockaddr_in *)&raddr); if (!((a->sin_port == b->sin_port) && (a->sin_addr.s_addr == b->sin_addr.s_addr))) { if (addr != NULL) { addr->len = sizeof(in_addr_t) + sizeof(in_port_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin_addr.s_addr, sizeof(in_addr_t)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(in_addr_t)), &b->sin_port, sizeof(in_port_t)); } break; } } else if (lep._iptcp->ai_family == AF_INET6) { struct sockaddr_in6 *a = ((struct sockaddr_in6 *)lep._iptcp->ai_addr); struct sockaddr_in6 *b = ((struct sockaddr_in6 *)&raddr); if ((a->sin6_port != b->sin6_port) || (memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(struct in6_addr)) != 0)) { if (addr != NULL) { addr->len = sizeof(struct in6_addr) + sizeof(in_port_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin6_addr.s6_addr, sizeof(struct in6_addr)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(struct in6_addr)), &b->sin6_port, sizeof(in_port_t)); } break; } } else { continue; } } while (1); return rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return sendto(sock._fd, ptr, len, 0, rep._iptcp->ai_addr, rep._iptcp->ai_addrlen); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_multicast_lwip.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZENOH_FREERTOS_LWIP) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include "lwip/ip4_addr.h" #include "lwip/netdb.h" #include "lwip/netif.h" #include "udp_multicast_lwip_common.h" static unsigned long __get_ip_from_iface(const char *iface, int sa_family, struct sockaddr **lsockaddr) { unsigned int addrlen = 0U; _ZP_UNUSED(sa_family); struct netif *netif = netif_find(iface); if (netif == NULL || !netif_is_up(netif)) { return 0; } struct sockaddr_in *lsockaddr_in = (struct sockaddr_in *)z_malloc(sizeof(struct sockaddr_in)); if (lsockaddr_in == NULL) { return 0; } (void)memset(lsockaddr_in, 0, sizeof(struct sockaddr_in)); const ip4_addr_t *ip4_addr = netif_ip4_addr(netif); inet_addr_from_ip4addr(&lsockaddr_in->sin_addr, ip_2_ip4(ip4_addr)); lsockaddr_in->sin_family = AF_INET; lsockaddr_in->sin_port = htons(0); addrlen = sizeof(struct sockaddr_in); *lsockaddr = (struct sockaddr *)lsockaddr_in; return addrlen; } static z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_lwip_udp_multicast_open(sock, rep, lep, tout, iface, __get_ip_from_iface); } static z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_lwip_udp_multicast_listen(sock, rep, tout, iface, join, __get_ip_from_iface); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_lwip_udp_multicast_close(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_lwip_udp_multicast_read_exact(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_lwip_udp_multicast_read(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_lwip_udp_multicast_write(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_multicast_lwip_common.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "udp_multicast_lwip_common.h" #include "zenoh-pico/config.h" #if defined(ZP_PLATFORM_SOCKET_LWIP) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include "lwip/netdb.h" #include "lwip/sockets.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/link/transport/lwip_socket.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_lwip_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface, _z_lwip_udp_multicast_iface_addr_fn get_ip_from_iface) { z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned long addrlen = get_ip_from_iface(iface, rep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { _z_lwip_socket_set(sock, socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol)); if (_z_lwip_socket_get(*sock) != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(_z_lwip_socket_get(*sock), lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (getsockname(_z_lwip_socket_get(*sock), lsockaddr, &addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); z_free(lsockaddr); return ret; } struct addrinfo *laddr = (struct addrinfo *)z_malloc(sizeof(struct addrinfo)); if (laddr == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); z_free(lsockaddr); return _Z_ERR_GENERIC; } laddr->ai_flags = 0; laddr->ai_family = rep._iptcp->ai_family; laddr->ai_socktype = rep._iptcp->ai_socktype; laddr->ai_protocol = rep._iptcp->ai_protocol; laddr->ai_addrlen = addrlen; laddr->ai_addr = lsockaddr; laddr->ai_canonname = NULL; laddr->ai_next = NULL; lep->_iptcp = laddr; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { z_free(lsockaddr); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_lwip_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join, _z_lwip_udp_multicast_iface_addr_fn get_ip_from_iface) { z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned int addrlen = get_ip_from_iface(iface, rep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { _z_lwip_socket_set(sock, socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol)); if (_z_lwip_socket_get(*sock) != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._iptcp->ai_family == AF_INET) { struct sockaddr_in address; (void)memset(&address, 0, sizeof(address)); address.sin_family = (sa_family_t)rep._iptcp->ai_family; address.sin_port = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_port; inet_pton(address.sin_family, "0.0.0.0", &address.sin_addr); if ((ret == _Z_RES_OK) && (bind(_z_lwip_socket_get(*sock), (struct sockaddr *)&address, sizeof(address)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = ((struct sockaddr_in *)lsockaddr)->sin_addr.s_addr; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (join != NULL) { char *joins = _z_str_clone(join); char *joins_cursor = joins; for (char *ip = strsep(&joins_cursor, "|"); ip != NULL; ip = strsep(&joins_cursor, "|")) { if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); inet_pton(rep._iptcp->ai_family, ip, &mreq.imr_multiaddr); mreq.imr_interface.s_addr = ((struct sockaddr_in *)lsockaddr)->sin_addr.s_addr; if ((ret == _Z_RES_OK) && (setsockopt(_z_lwip_socket_get(*sock), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } } z_free(joins); } if (ret != _Z_RES_OK) { close(_z_lwip_socket_get(*sock)); _z_lwip_socket_set(sock, -1); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } z_free(lsockaddr); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_lwip_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { if (_z_lwip_socket_get(*sockrecv) >= 0) { if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(_z_lwip_socket_get(*sockrecv), IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); } } _ZP_UNUSED(lep); if (_z_lwip_socket_get(*sockrecv) >= 0) { close(_z_lwip_socket_get(*sockrecv)); _z_lwip_socket_set(sockrecv, -1); } if (_z_lwip_socket_get(*socksend) >= 0) { close(_z_lwip_socket_get(*socksend)); _z_lwip_socket_set(socksend, -1); } } size_t _z_lwip_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { struct sockaddr_storage raddr; unsigned long replen = sizeof(struct sockaddr_storage); ssize_t rb = 0; do { rb = recvfrom(_z_lwip_socket_get(sock), ptr, len, 0, (struct sockaddr *)&raddr, &replen); if (rb < (ssize_t)0) { return SIZE_MAX; } if (lep._iptcp->ai_family == AF_INET) { struct sockaddr_in *a = ((struct sockaddr_in *)lep._iptcp->ai_addr); struct sockaddr_in *b = ((struct sockaddr_in *)&raddr); if (!((a->sin_port == b->sin_port) && (a->sin_addr.s_addr == b->sin_addr.s_addr))) { if (addr != NULL) { addr->len = sizeof(in_addr_t) + sizeof(in_port_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin_addr.s_addr, sizeof(in_addr_t)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(in_addr_t)), &b->sin_port, sizeof(in_port_t)); } break; } } else { continue; } } while (1); return (size_t)rb; } size_t _z_lwip_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_lwip_udp_multicast_read(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, (ptrdiff_t)rb); } while (n != len); return n; } size_t _z_lwip_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return (size_t)sendto(_z_lwip_socket_get(sock), ptr, len, 0, rep._iptcp->ai_addr, rep._iptcp->ai_addrlen); } #endif /* defined(ZP_PLATFORM_SOCKET_LWIP) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) */ ================================================ FILE: src/link/transport/udp/udp_multicast_lwip_common.h ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef ZENOH_PICO_LINK_TRANSPORT_DATAGRAM_UDP_MULTICAST_LWIP_COMMON_H #define ZENOH_PICO_LINK_TRANSPORT_DATAGRAM_UDP_MULTICAST_LWIP_COMMON_H #include "zenoh-pico/link/transport/udp_multicast.h" #ifdef __cplusplus extern "C" { #endif typedef unsigned long (*_z_lwip_udp_multicast_iface_addr_fn)(const char *iface, int sa_family, struct sockaddr **lsockaddr); z_result_t _z_lwip_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface, _z_lwip_udp_multicast_iface_addr_fn get_ip_from_iface); z_result_t _z_lwip_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join, _z_lwip_udp_multicast_iface_addr_fn get_ip_from_iface); void _z_lwip_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep); size_t _z_lwip_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr); size_t _z_lwip_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr); size_t _z_lwip_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep); #ifdef __cplusplus } #endif #endif /* ZENOH_PICO_LINK_TRANSPORT_DATAGRAM_UDP_MULTICAST_LWIP_COMMON_H */ ================================================ FILE: src/link/transport/udp/udp_multicast_mbed.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_MBED) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include extern "C" { #include #include "zenoh-pico/link/transport/udp_multicast.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { z_result_t ret = _Z_RES_OK; _ZP_UNUSED(rep); (void)(lep); (void)(iface); sock->_udp = new UDPSocket(); sock->_udp->set_timeout(tout); // flawfinder: ignore if ((ret == _Z_RES_OK) && (sock->_udp->open(NetworkInterface::get_default_instance()) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete sock->_udp; sock->_udp = NULL; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { (void)iface; (void)join; z_result_t ret = _Z_RES_OK; sock->_udp = new UDPSocket(); sock->_udp->set_timeout(tout); // flawfinder: ignore if ((ret == _Z_RES_OK) && (sock->_udp->open(NetworkInterface::get_default_instance()) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (sock->_udp->bind(rep._iptcp->get_port()) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (sock->_udp->join_multicast_group(*rep._iptcp) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { sock->_udp->close(); delete sock->_udp; sock->_udp = NULL; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _ZP_UNUSED(lep); if (sockrecv->_udp != NULL) { sockrecv->_udp->leave_multicast_group(*rep._iptcp); sockrecv->_udp->close(); delete sockrecv->_udp; sockrecv->_udp = NULL; } if (socksend->_udp != NULL) { socksend->_udp->close(); delete socksend->_udp; socksend->_udp = NULL; } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { _ZP_UNUSED(lep); SocketAddress raddr; nsapi_size_or_error_t rb = 0; do { rb = sock._udp->recvfrom(&raddr, ptr, len); if (rb < (nsapi_size_or_error_t)0) { rb = SIZE_MAX; break; } if (raddr.get_ip_version() == NSAPI_IPv4) { addr->len = NSAPI_IPv4_BYTES + sizeof(uint16_t); // flawfinder: ignore (void)memcpy(const_cast(addr->start), raddr.get_ip_bytes(), NSAPI_IPv4_BYTES); uint16_t port = raddr.get_port(); // flawfinder: ignore (void)memcpy(const_cast(addr->start + NSAPI_IPv4_BYTES), &port, sizeof(uint16_t)); break; } else if (raddr.get_ip_version() == NSAPI_IPv6) { addr->len = NSAPI_IPv6_BYTES + sizeof(uint16_t); // flawfinder: ignore (void)memcpy(const_cast(addr->start), raddr.get_ip_bytes(), NSAPI_IPv6_BYTES); uint16_t port = raddr.get_port(); // flawfinder: ignore (void)memcpy(const_cast(addr->start + NSAPI_IPv6_BYTES), &port, sizeof(uint16_t)); break; } else { continue; } } while (1); return rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return sock._udp->sendto(*rep._iptcp, ptr, len); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } } // extern "C" #endif ================================================ FILE: src/link/transport/udp/udp_multicast_opencr.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_OPENCR) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include #include #include #include #include extern "C" { #include "zenoh-pico/link/transport/udp_multicast.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { z_result_t ret = _Z_RES_OK; (void)(rep); (void)(tout); (void)(iface); sock->_udp = new WiFiUDP(); if (!sock->_udp->begin(55555)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } lep->_iptcp._addr = new IPAddress(); if (ret != _Z_RES_OK) { delete sock->_udp; sock->_udp = NULL; delete lep->_iptcp._addr; lep->_iptcp._addr = NULL; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { (void)tout; (void)iface; (void)join; z_result_t ret = _Z_RES_OK; sock->_udp = new WiFiUDP(); if (!sock->_udp->beginMulticast(*rep._iptcp._addr, rep._iptcp._port)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete sock->_udp; sock->_udp = NULL; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _ZP_UNUSED(rep); _ZP_UNUSED(lep); if (sockrecv->_udp != NULL) { sockrecv->_udp->stop(); delete sockrecv->_udp; sockrecv->_udp = NULL; } if (socksend->_udp != NULL) { socksend->_udp->stop(); delete socksend->_udp; socksend->_udp = NULL; } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { _ZP_UNUSED(lep); ssize_t rb = 0; do { rb = sock._udp->parsePacket(); } while (rb == 0); if (rb <= (ssize_t)len) { // flawfinder: ignore if (sock._udp->read(ptr, rb) == rb) { if (addr != NULL) { IPAddress rip = sock._udp->remoteIP(); uint16_t rport = sock._udp->remotePort(); addr->len = 4U + sizeof(uint16_t); uint8_t *dst = const_cast(addr->start); dst[0] = rip[0]; dst[1] = rip[1]; dst[2] = rip[2]; dst[3] = rip[3]; const uint8_t *port_bytes = (const uint8_t *)&rport; dst[4] = port_bytes[0]; dst[5] = port_bytes[1]; } } else { rb = 0; } } return rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { sock._udp->beginPacket(*rep._iptcp._addr, rep._iptcp._port); sock._udp->write(ptr, len); sock._udp->endPacket(); return len; } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } } // extern "C" #endif ================================================ FILE: src/link/transport/udp/udp_multicast_posix.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZP_PLATFORM_SOCKET_POSIX) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include #include #include #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static unsigned int __get_ip_from_iface(const char *iface, int sa_family, struct sockaddr **lsockaddr) { unsigned int addrlen = 0U; struct ifaddrs *l_ifaddr = NULL; if (getifaddrs(&l_ifaddr) != -1) { struct ifaddrs *tmp = NULL; for (tmp = l_ifaddr; tmp != NULL; tmp = tmp->ifa_next) { if (_z_str_eq(tmp->ifa_name, iface) == true) { if (tmp->ifa_addr->sa_family == sa_family) { if (tmp->ifa_addr->sa_family == AF_INET) { *lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in)); if (lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(struct sockaddr_in)); // flawfinder: ignore (void)memcpy(*lsockaddr, tmp->ifa_addr, sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); } } else if (tmp->ifa_addr->sa_family == AF_INET6) { *lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in6)); if (lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(struct sockaddr_in6)); // flawfinder: ignore (void)memcpy(*lsockaddr, tmp->ifa_addr, sizeof(struct sockaddr_in6)); addrlen = sizeof(struct sockaddr_in6); } } else { continue; } break; } } } freeifaddrs(l_ifaddr); } return addrlen; } z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned int addrlen = __get_ip_from_iface(iface, rep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = (time_t)(tout / (uint32_t)1000); tv.tv_usec = (suseconds_t)((tout % (uint32_t)1000) * (uint32_t)1000); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(sock->_fd, lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (getsockname(sock->_fd, lsockaddr, &addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #ifndef UNIX_NO_MULTICAST_IF if (lsockaddr->sa_family == AF_INET) { if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in *)lsockaddr)->sin_addr, sizeof(struct in_addr)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (lsockaddr->sa_family == AF_INET6) { int ifindex = (int)if_nametoindex(iface); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return ret; } struct addrinfo *laddr = (struct addrinfo *)z_malloc(sizeof(struct addrinfo)); if (laddr == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return _Z_ERR_GENERIC; } laddr->ai_flags = 0; laddr->ai_family = rep._iptcp->ai_family; laddr->ai_socktype = rep._iptcp->ai_socktype; laddr->ai_protocol = rep._iptcp->ai_protocol; laddr->ai_addrlen = addrlen; laddr->ai_addr = lsockaddr; laddr->ai_canonname = NULL; laddr->ai_next = NULL; lep->_iptcp = laddr; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { z_free(lsockaddr); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned int addrlen = __get_ip_from_iface(iface, rep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = (time_t)(tout / (uint32_t)1000); tv.tv_usec = (suseconds_t)((tout % (uint32_t)1000) * (uint32_t)1000); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int value = true; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #ifdef SO_REUSEPORT if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif if (rep._iptcp->ai_family == AF_INET) { struct sockaddr_in address; (void)memset(&address, 0, sizeof(address)); address.sin_family = (sa_family_t)rep._iptcp->ai_family; address.sin_port = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_port; inet_pton(address.sin_family, "0.0.0.0", &address.sin_addr); if ((ret == _Z_RES_OK) && (bind(sock->_fd, (struct sockaddr *)&address, sizeof(address)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._iptcp->ai_family == AF_INET6) { struct sockaddr_in6 address; (void)memset(&address, 0, sizeof(address)); address.sin6_family = (sa_family_t)rep._iptcp->ai_family; address.sin6_port = ((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_port; inet_pton(address.sin6_family, "::", &address.sin6_addr); if ((ret == _Z_RES_OK) && (bind(sock->_fd, (struct sockaddr *)&address, sizeof(address)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = ((struct sockaddr_in *)lsockaddr)->sin_addr.s_addr; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._iptcp->ai_family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr, sizeof(struct in6_addr)); mreq.ipv6mr_interface = if_nametoindex(iface); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (join != NULL) { char *joins = _z_str_clone(join); char *joins_cursor = joins; for (char *ip = strsep(&joins_cursor, "|"); ip != NULL; ip = strsep(&joins_cursor, "|")) { if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); inet_pton(rep._iptcp->ai_family, ip, &mreq.imr_multiaddr); mreq.imr_interface.s_addr = ((struct sockaddr_in *)lsockaddr)->sin_addr.s_addr; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._iptcp->ai_family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); inet_pton(rep._iptcp->ai_family, ip, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = if_nametoindex(iface); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } } z_free(joins); } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } z_free(lsockaddr); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { if (sockrecv->_fd >= 0) { if (rep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockrecv->_fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); } else if (rep._iptcp->ai_family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr, sizeof(struct in6_addr)); setsockopt(sockrecv->_fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)); } else { /* Do nothing. */ } } #if defined(ZENOH_LINUX) if (lep._iptcp != NULL) { z_free(lep._iptcp->ai_addr); } #else _ZP_UNUSED(lep); #endif if (sockrecv->_fd >= 0) { close(sockrecv->_fd); sockrecv->_fd = -1; } if (socksend->_fd >= 0) { close(socksend->_fd); socksend->_fd = -1; } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { struct sockaddr_storage raddr; unsigned int replen = sizeof(struct sockaddr_storage); ssize_t rb = 0; do { rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &replen); if (rb < (ssize_t)0) { return SIZE_MAX; } if (lep._iptcp->ai_family == AF_INET) { struct sockaddr_in *a = ((struct sockaddr_in *)lep._iptcp->ai_addr); struct sockaddr_in *b = ((struct sockaddr_in *)&raddr); if (!((a->sin_port == b->sin_port) && (a->sin_addr.s_addr == b->sin_addr.s_addr))) { if (addr != NULL) { assert(addr->len >= sizeof(in_addr_t) + sizeof(in_port_t)); addr->len = sizeof(in_addr_t) + sizeof(in_port_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin_addr.s_addr, sizeof(in_addr_t)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(in_addr_t)), &b->sin_port, sizeof(in_port_t)); } break; } } else if (lep._iptcp->ai_family == AF_INET6) { struct sockaddr_in6 *a = ((struct sockaddr_in6 *)lep._iptcp->ai_addr); struct sockaddr_in6 *b = ((struct sockaddr_in6 *)&raddr); if (!((a->sin6_port == b->sin6_port) && (memcmp(a->sin6_addr.s6_addr, b->sin6_addr.s6_addr, sizeof(struct in6_addr)) == 0))) { if (addr != NULL) { assert(addr->len >= sizeof(struct in6_addr) + sizeof(in_port_t)); addr->len = sizeof(struct in6_addr) + sizeof(in_port_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin6_addr.s6_addr, sizeof(struct in6_addr)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(struct in6_addr)), &b->sin6_port, sizeof(in_port_t)); } break; } } else { continue; } } while (1); return (size_t)rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, (ptrdiff_t)rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return (size_t)sendto(sock._fd, ptr, len, 0, rep._iptcp->ai_addr, rep._iptcp->ai_addrlen); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_multicast_rpi_pico.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZENOH_RPI_PICO) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include "lwip/ip4_addr.h" #include "lwip/netdb.h" #include "pico/cyw43_arch.h" #include "udp_multicast_lwip_common.h" static unsigned long __get_ip_from_iface(const char *iface, int sa_family, struct sockaddr **lsockaddr) { _ZP_UNUSED(iface); _ZP_UNUSED(sa_family); unsigned int addrlen = 0U; struct netif *netif = &cyw43_state.netif[CYW43_ITF_STA]; if (!netif_is_up(netif)) { return 0; } struct sockaddr_in *lsockaddr_in = (struct sockaddr_in *)z_malloc(sizeof(struct sockaddr_in)); if (lsockaddr_in == NULL) { return 0; } (void)memset(lsockaddr_in, 0, sizeof(struct sockaddr_in)); const ip4_addr_t *ip4_addr = netif_ip4_addr(netif); inet_addr_from_ip4addr(&lsockaddr_in->sin_addr, ip_2_ip4(ip4_addr)); lsockaddr_in->sin_family = AF_INET; lsockaddr_in->sin_port = htons(0); addrlen = sizeof(struct sockaddr_in); *lsockaddr = (struct sockaddr *)lsockaddr_in; return addrlen; } static z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_lwip_udp_multicast_open(sock, rep, lep, tout, iface, __get_ip_from_iface); } static z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_lwip_udp_multicast_listen(sock, rep, tout, iface, join, __get_ip_from_iface); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_lwip_udp_multicast_close(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_lwip_udp_multicast_read_exact(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_lwip_udp_multicast_read(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_lwip_udp_multicast_write(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_multicast_windows.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZP_PLATFORM_SOCKET_WINDOWS) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static WSADATA _z_udp_multicast_windows_wsa_data; static unsigned int __get_ip_from_iface(const char *iface, int sa_family, SOCKADDR **lsockaddr) { unsigned int addrlen = 0U; unsigned long outBufLen = 15000; IP_ADAPTER_ADDRESSES *l_ifaddr = (IP_ADAPTER_ADDRESSES *)z_malloc(outBufLen); if (l_ifaddr != NULL) { if (GetAdaptersAddresses(sa_family, 0, NULL, l_ifaddr, &outBufLen) != ERROR_BUFFER_OVERFLOW) { for (IP_ADAPTER_ADDRESSES *tmp = l_ifaddr; tmp != NULL; tmp = tmp->Next) { if (_z_str_eq(tmp->AdapterName, iface) == true) { if (sa_family == AF_INET) { *lsockaddr = (SOCKADDR *)z_malloc(sizeof(SOCKADDR_IN)); if (lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(SOCKADDR_IN)); // flawfinder: ignore (void)memcpy(*lsockaddr, tmp->FirstUnicastAddress->Address.lpSockaddr, sizeof(SOCKADDR_IN)); addrlen = sizeof(SOCKADDR_IN); } } else if (sa_family == AF_INET6) { *lsockaddr = (SOCKADDR *)z_malloc(sizeof(SOCKADDR_IN6)); if (lsockaddr != NULL) { (void)memset(*lsockaddr, 0, sizeof(SOCKADDR_IN6)); // flawfinder: ignore (void)memcpy(*lsockaddr, tmp->FirstUnicastAddress->Address.lpSockaddr, sizeof(SOCKADDR_IN6)); addrlen = sizeof(SOCKADDR_IN6); } } else { continue; } break; } } } z_free(l_ifaddr); l_ifaddr = NULL; } return addrlen; } z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_udp_multicast_windows_wsa_data) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } SOCKADDR *lsockaddr = NULL; unsigned int addrlen = __get_ip_from_iface(iface, rep._ep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { sock->_sock._fd = socket(rep._ep._iptcp->ai_family, rep._ep._iptcp->ai_socktype, rep._ep._iptcp->ai_protocol); if (sock->_sock._fd != INVALID_SOCKET) { DWORD tv = tout; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._ep._iptcp->ai_family == AF_INET) { SOCKADDR_IN address = { .sin_family = AF_INET, .sin_port = htons(0), .sin_addr.s_addr = htonl(INADDR_ANY), .sin_zero = {0}}; if ((ret == _Z_RES_OK) && (bind(sock->_sock._fd, (SOCKADDR *)&address, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._ep._iptcp->ai_family == AF_INET6) { SOCKADDR_IN6 address = {.sin6_family = AF_INET6, .sin6_port = htons(0), 0, .sin6_addr = {{{0}}}, 0}; if ((ret == _Z_RES_OK) && (bind(sock->_sock._fd, (SOCKADDR *)&address, sizeof(SOCKADDR_IN6)) == SOCKET_ERROR)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (getsockname(sock->_sock._fd, lsockaddr, (int *)&addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._ep._iptcp->ai_family == AF_INET) { if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&((SOCKADDR_IN *)lsockaddr)->sin_addr.s_addr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._ep._iptcp->ai_family == AF_INET6) { if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char *)&((SOCKADDR_IN6 *)lsockaddr)->sin6_scope_id, sizeof(ULONG)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); z_free(lsockaddr); return ret; } ADDRINFOA *laddr = (ADDRINFOA *)z_malloc(sizeof(ADDRINFOA)); if (laddr == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); z_free(lsockaddr); return _Z_ERR_GENERIC; } laddr->ai_flags = 0; laddr->ai_family = rep._ep._iptcp->ai_family; laddr->ai_socktype = rep._ep._iptcp->ai_socktype; laddr->ai_protocol = rep._ep._iptcp->ai_protocol; laddr->ai_addrlen = addrlen; laddr->ai_addr = lsockaddr; laddr->ai_canonname = NULL; laddr->ai_next = NULL; lep->_ep._iptcp = laddr; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { z_free(lsockaddr); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { (void)join; z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_udp_multicast_windows_wsa_data) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } SOCKADDR *lsockaddr = NULL; unsigned int addrlen = __get_ip_from_iface(iface, rep._ep._iptcp->ai_family, &lsockaddr); if (addrlen != 0U) { sock->_sock._fd = socket(rep._ep._iptcp->ai_family, rep._ep._iptcp->ai_socktype, rep._ep._iptcp->ai_protocol); if (sock->_sock._fd != INVALID_SOCKET) { DWORD tv = tout; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._ep._iptcp->ai_family == AF_INET) { SOCKADDR_IN address = {.sin_family = AF_INET, .sin_port = ((SOCKADDR_IN *)rep._ep._iptcp->ai_addr)->sin_port, .sin_addr = {0}, .sin_zero = {0}}; if ((ret == _Z_RES_OK) && (bind(sock->_sock._fd, (SOCKADDR *)&address, sizeof address) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._ep._iptcp->ai_family == AF_INET6) { SOCKADDR_IN6 address = {.sin6_family = AF_INET6, .sin6_port = ((SOCKADDR_IN6 *)rep._ep._iptcp->ai_addr)->sin6_port, 0, .sin6_addr = {{{0}}}, 0}; if ((ret == _Z_RES_OK) && (bind(sock->_sock._fd, (SOCKADDR *)&address, sizeof address) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (rep._ep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((SOCKADDR_IN *)rep._ep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = ((SOCKADDR_IN *)lsockaddr)->sin_addr.s_addr; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._ep._iptcp->ai_family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.ipv6mr_multiaddr, &((SOCKADDR_IN6 *)rep._ep._iptcp->ai_addr)->sin6_addr, sizeof(IN6_ADDR)); mreq.ipv6mr_interface = if_nametoindex(iface); if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&mreq, sizeof(mreq)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } z_free(lsockaddr); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _ZP_UNUSED(lep); if (sockrecv->_sock._fd != INVALID_SOCKET) { if (rep._ep._iptcp->ai_family == AF_INET) { struct ip_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); mreq.imr_multiaddr.s_addr = ((SOCKADDR_IN *)rep._ep._iptcp->ai_addr)->sin_addr.s_addr; mreq.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockrecv->_sock._fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)); } else if (rep._ep._iptcp->ai_family == AF_INET6) { struct ipv6_mreq mreq; (void)memset(&mreq, 0, sizeof(mreq)); // flawfinder: ignore (void)memcpy(&mreq.ipv6mr_multiaddr, &((SOCKADDR_IN6 *)rep._ep._iptcp->ai_addr)->sin6_addr, sizeof(struct in6_addr)); setsockopt(sockrecv->_sock._fd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (const char *)&mreq, sizeof(mreq)); } else { /* Do nothing. */ } } if (sockrecv->_sock._fd != INVALID_SOCKET) { closesocket(sockrecv->_sock._fd); sockrecv->_sock._fd = INVALID_SOCKET; WSACleanup(); } if (socksend->_sock._fd != INVALID_SOCKET) { closesocket(socksend->_sock._fd); socksend->_sock._fd = INVALID_SOCKET; WSACleanup(); } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { SOCKADDR_STORAGE raddr; unsigned int replen = sizeof(SOCKADDR_STORAGE); size_t rb = 0; do { rb = recvfrom(sock._sock._fd, (char *)ptr, (int)len, 0, (SOCKADDR *)&raddr, (int *)&replen); if (rb < (size_t)0) { rb = SIZE_MAX; break; } if (lep._ep._iptcp->ai_family == AF_INET) { SOCKADDR_IN *a = ((SOCKADDR_IN *)lep._ep._iptcp->ai_addr); SOCKADDR_IN *b = ((SOCKADDR_IN *)&raddr); if (!((a->sin_port == b->sin_port) && (a->sin_addr.s_addr == b->sin_addr.s_addr))) { if (addr != NULL) { addr->len = sizeof(IN_ADDR) + sizeof(USHORT); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin_addr.s_addr, sizeof(IN_ADDR)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(IN_ADDR)), &b->sin_port, sizeof(USHORT)); } break; } } else if (lep._ep._iptcp->ai_family == AF_INET6) { SOCKADDR_IN6 *a = ((SOCKADDR_IN6 *)lep._ep._iptcp->ai_addr); SOCKADDR_IN6 *b = ((SOCKADDR_IN6 *)&raddr); if (!((a->sin6_port == b->sin6_port) && (memcmp(a->sin6_addr.s6_addr, b->sin6_addr.s6_addr, sizeof(struct in6_addr)) == 0))) { if (addr != NULL) { addr->len = sizeof(struct in6_addr) + sizeof(USHORT); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin6_addr.s6_addr, sizeof(struct in6_addr)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(struct in6_addr)), &b->sin6_port, sizeof(USHORT)); } break; } } else { continue; } } while (1); return rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return sendto(sock._sock._fd, (const char *)ptr, (int)len, 0, rep._ep._iptcp->ai_addr, (int)rep._ep._iptcp->ai_addrlen); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_multicast_zephyr.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/udp_multicast.h" #if defined(ZP_PLATFORM_SOCKET_ZEPHYR) && (Z_FEATURE_LINK_UDP_MULTICAST == 1) #include #include #include #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_open_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { _ZP_UNUSED(iface); z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned int addrlen = 0; if (rep._iptcp->ai_family == AF_INET) { lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in)); if (lsockaddr != NULL) { (void)memset(lsockaddr, 0, sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); struct sockaddr_in *c_laddr = (struct sockaddr_in *)lsockaddr; c_laddr->sin_family = AF_INET; c_laddr->sin_addr.s_addr = INADDR_ANY; c_laddr->sin_port = htons(INADDR_ANY); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._iptcp->ai_family == AF_INET6) { lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in6)); if (lsockaddr != NULL) { (void)memset(lsockaddr, 0, sizeof(struct sockaddr_in6)); addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 *c_laddr = (struct sockaddr_in6 *)lsockaddr; c_laddr->sin6_family = AF_INET6; c_laddr->sin6_addr = in6addr_any; c_laddr->sin6_port = htons(INADDR_ANY); // c_laddr->sin6_scope_id; // Not needed to be defined } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (addrlen != 0U) { sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { // FIXME: setting the setsockopt is consistently failing. Commenting it _Z_ERROR_LOG(_Z_ERR_GENERIC); // until further inspection. ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(sock->_fd, lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } // Get the randomly assigned port used to discard loopback messages if ((ret == _Z_RES_OK) && (getsockname(sock->_fd, lsockaddr, &addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return ret; } // Create lep endpoint struct addrinfo *laddr = (struct addrinfo *)z_malloc(sizeof(struct addrinfo)); if (laddr == NULL) { _Z_ERROR_LOG(_Z_ERR_GENERIC); close(sock->_fd); sock->_fd = -1; z_free(lsockaddr); return _Z_ERR_GENERIC; } laddr->ai_flags = 0; laddr->ai_family = rep._iptcp->ai_family; laddr->ai_socktype = rep._iptcp->ai_socktype; laddr->ai_protocol = rep._iptcp->ai_protocol; laddr->ai_addrlen = addrlen; laddr->ai_addr = lsockaddr; laddr->ai_canonname = NULL; laddr->ai_next = NULL; lep->_iptcp = laddr; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { z_free(lsockaddr); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_listen_udp_multicast(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { (void)iface; (void)join; z_result_t ret = _Z_RES_OK; struct sockaddr *lsockaddr = NULL; unsigned int addrlen = 0; if (rep._iptcp->ai_family == AF_INET) { lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in)); if (lsockaddr != NULL) { (void)memset(lsockaddr, 0, sizeof(struct sockaddr_in)); addrlen = sizeof(struct sockaddr_in); struct sockaddr_in *c_laddr = (struct sockaddr_in *)lsockaddr; c_laddr->sin_family = AF_INET; c_laddr->sin_addr.s_addr = INADDR_ANY; c_laddr->sin_port = ((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_port; } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else if (rep._iptcp->ai_family == AF_INET6) { lsockaddr = (struct sockaddr *)z_malloc(sizeof(struct sockaddr_in6)); if (lsockaddr != NULL) { (void)memset(lsockaddr, 0, sizeof(struct sockaddr_in6)); addrlen = sizeof(struct sockaddr_in6); struct sockaddr_in6 *c_laddr = (struct sockaddr_in6 *)lsockaddr; c_laddr->sin6_family = AF_INET6; c_laddr->sin6_addr = in6addr_any; c_laddr->sin6_port = ((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_port; // c_laddr->sin6_scope_id; // Not needed to be defined } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } sock->_fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_fd != -1) { int optflag = 1; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optflag, sizeof(optflag)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { // FIXME: setting the setsockopt is consistently failing. Commenting it _Z_ERROR_LOG(_Z_ERR_GENERIC); // until further inspection. ret = _Z_ERR_GENERIC; } if ((ret == _Z_RES_OK) && (bind(sock->_fd, lsockaddr, addrlen) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } // FIXME: iface passed into the locator is being ignored // default if used instead if (ret != _Z_RES_OK) { struct net_if *ifa = NULL; ifa = net_if_get_default(); if (ifa != NULL) { // Join the multicast group if (rep._iptcp->ai_family == AF_INET) { struct net_if_mcast_addr *mcast = NULL; mcast = net_if_ipv4_maddr_add(ifa, &((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr); if (!mcast) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if KERNEL_VERSION_MAJOR == 3 && KERNEL_VERSION_MINOR > 3 || KERNEL_VERSION_MAJOR >= 4 net_if_ipv4_maddr_join(ifa, mcast); #else net_if_ipv4_maddr_join(mcast); #endif } else if (rep._iptcp->ai_family == AF_INET6) { struct net_if_mcast_addr *mcast = NULL; mcast = net_if_ipv6_maddr_add(ifa, &((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr); if (!mcast) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #if KERNEL_VERSION_MAJOR == 3 && KERNEL_VERSION_MINOR > 3 || KERNEL_VERSION_MAJOR >= 4 net_if_ipv6_maddr_join(ifa, mcast); #else net_if_ipv6_maddr_join(mcast); #endif } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } z_free(lsockaddr); } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } void _z_close_udp_multicast(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _ZP_UNUSED(lep); if (sockrecv->_fd >= 0) { // FIXME: iface passed into the locator is being ignored // default if used instead struct net_if *ifa = NULL; ifa = net_if_get_default(); if (ifa != NULL) { struct net_if_mcast_addr *mcast = NULL; if (rep._iptcp->ai_family == AF_INET) { mcast = net_if_ipv4_maddr_add(ifa, &((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr); if (mcast != NULL) { #if KERNEL_VERSION_MAJOR == 3 && KERNEL_VERSION_MINOR > 3 || KERNEL_VERSION_MAJOR >= 4 net_if_ipv4_maddr_leave(ifa, mcast); #else net_if_ipv4_maddr_leave(mcast); #endif net_if_ipv4_maddr_rm(ifa, &((struct sockaddr_in *)rep._iptcp->ai_addr)->sin_addr); } else { // Do nothing. The socket will be closed in any case. } } else if (rep._iptcp->ai_family == AF_INET6) { mcast = net_if_ipv6_maddr_add(ifa, &((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr); if (mcast != NULL) { #if KERNEL_VERSION_MAJOR == 3 && KERNEL_VERSION_MINOR > 3 || KERNEL_VERSION_MAJOR >= 4 net_if_ipv6_maddr_leave(ifa, mcast); #else net_if_ipv6_maddr_leave(mcast); #endif net_if_ipv6_maddr_rm(ifa, &((struct sockaddr_in6 *)rep._iptcp->ai_addr)->sin6_addr); } else { // Do nothing. The socket will be closed in any case. } } else { // Do nothing. It must never not enter here. // Required to be compliant with MISRA 15.7 rule } } } if (sockrecv->_fd >= 0) { close(sockrecv->_fd); sockrecv->_fd = -1; } if (socksend->_fd >= 0) { close(socksend->_fd); socksend->_fd = -1; } } size_t _z_read_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { struct sockaddr_storage raddr; unsigned int raddrlen = sizeof(struct sockaddr_storage); ssize_t rb = 0; do { rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &raddrlen); if (rb < (ssize_t)0) { rb = SIZE_MAX; break; } if (lep._iptcp->ai_family == AF_INET) { struct sockaddr_in *a = ((struct sockaddr_in *)lep._iptcp->ai_addr); struct sockaddr_in *b = ((struct sockaddr_in *)&raddr); if (!((a->sin_port == b->sin_port) && (a->sin_addr.s_addr == b->sin_addr.s_addr))) { if (addr != NULL) { addr->len = sizeof(uint32_t) + sizeof(uint16_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin_addr.s_addr, sizeof(uint32_t)); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + sizeof(uint32_t)), &b->sin_port, sizeof(uint16_t)); } break; } } else if (lep._iptcp->ai_family == AF_INET6) { struct sockaddr_in6 *a = ((struct sockaddr_in6 *)lep._iptcp->ai_addr); struct sockaddr_in6 *b = ((struct sockaddr_in6 *)&raddr); if (!((a->sin6_port == b->sin6_port) && (memcmp(a->sin6_addr.s6_addr, b->sin6_addr.s6_addr, sizeof(uint32_t) * 4UL) == 0))) { // If addr is not NULL, it means that the raddr was requested by the // upper-layers if (addr != NULL) { addr->len = (sizeof(uint32_t) * 4UL) + sizeof(uint16_t); // flawfinder: ignore (void)memcpy((uint8_t *)addr->start, &b->sin6_addr.s6_addr, sizeof(uint32_t) * 4UL); // flawfinder: ignore (void)memcpy((uint8_t *)(addr->start + (sizeof(uint32_t) * 4UL)), &b->sin6_port, sizeof(uint16_t)); } break; } } else { continue; // FIXME: support error report on invalid packet to the upper // layer } } while (1); return rb; } size_t _z_read_exact_udp_multicast(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *addr) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_read_udp_multicast(sock, pos, len - n, lep, addr); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } size_t _z_send_udp_multicast(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, _z_sys_net_endpoint_t rep) { return sendto(sock._fd, ptr, len, 0, rep._iptcp->ai_addr, rep._iptcp->ai_addrlen); } z_result_t _z_udp_multicast_endpoint_init_from_address(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_udp_multicast_default_endpoint_init_from_address(ep, address); } void _z_udp_multicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_multicast_default_endpoint_clear(ep); } z_result_t _z_udp_multicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, _z_sys_net_endpoint_t *lep, uint32_t tout, const char *iface) { return _z_open_udp_multicast(sock, rep, lep, tout, iface); } z_result_t _z_udp_multicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout, const char *iface, const char *join) { return _z_listen_udp_multicast(sock, rep, tout, iface, join); } void _z_udp_multicast_close(_z_sys_net_socket_t *sockrecv, _z_sys_net_socket_t *socksend, const _z_sys_net_endpoint_t rep, const _z_sys_net_endpoint_t lep) { _z_close_udp_multicast(sockrecv, socksend, rep, lep); } size_t _z_udp_multicast_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_exact_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t lep, _z_slice_t *ep) { return _z_read_udp_multicast(sock, ptr, len, lep, ep); } size_t _z_udp_multicast_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t rep) { return _z_send_udp_multicast(sock, ptr, len, rep); } #endif ================================================ FILE: src/link/transport/udp/udp_opencr.cpp ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #if defined(ZP_PLATFORM_SOCKET_OPENCR) #include #include #include #include extern "C" { #include "zenoh-pico/link/transport/udp_unicast.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_opencr_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; ep->_iptcp._addr = new IPAddress(); if (!ep->_iptcp._addr->fromString(s_address)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } ep->_iptcp._port = strtoul(s_port, NULL, 10); if ((ep->_iptcp._port < (uint32_t)1) || (ep->_iptcp._port > (uint32_t)65535)) { // Port numbers should range from 1 to 65535 _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete ep->_iptcp._addr; ep->_iptcp._addr = NULL; } return ret; } static void _z_udp_opencr_endpoint_clear(_z_sys_net_endpoint_t *ep) { delete ep->_iptcp._addr; ep->_iptcp._addr = NULL; } static z_result_t _z_udp_opencr_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); z_result_t ret = _Z_RES_OK; sock->_udp = new WiFiUDP(); if (!sock->_udp->begin(7447)) { // FIXME: make it random _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { delete sock->_udp; sock->_udp = NULL; } return ret; } static z_result_t _z_udp_opencr_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_opencr_close(_z_sys_net_socket_t *sock) { if (sock->_udp != NULL) { sock->_udp->stop(); delete sock->_udp; sock->_udp = NULL; } } static size_t _z_udp_opencr_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { ssize_t rb = 0; do { rb = sock._udp->parsePacket(); } while (rb == 0); if ((rb <= 0) || ((size_t)rb > len)) { return 0; } // flawfinder: ignore if (sock._udp->read(ptr, (size_t)rb) != rb) { rb = 0; } return rb; } static size_t _z_udp_opencr_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_opencr_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_opencr_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { sock._udp->beginPacket(*endpoint._iptcp._addr, endpoint._iptcp._port); sock._udp->write(ptr, len); sock._udp->endPacket(); return len; } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_opencr_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_opencr_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_opencr_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_opencr_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_opencr_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_opencr_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_opencr_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_opencr_write(sock, ptr, len, endpoint); } } // extern "C" #endif /* defined(ZP_PLATFORM_SOCKET_OPENCR) */ ================================================ FILE: src/link/transport/udp/udp_posix.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_POSIX) #include #include #include #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_posix_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_UDP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_posix_endpoint_clear(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_iptcp); } static z_result_t _z_udp_posix_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = (time_t)(tout / (uint32_t)1000); tv.tv_usec = (suseconds_t)((tout % (uint32_t)1000) * (uint32_t)1000); if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_posix_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_posix_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } static size_t _z_udp_posix_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { struct sockaddr_storage raddr; unsigned int addrlen = sizeof(struct sockaddr_storage); ssize_t rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &addrlen); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_posix_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_posix_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n += rb; pos = _z_ptr_u8_offset(pos, (ptrdiff_t)rb); } while (n != len); return n; } static size_t _z_udp_posix_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return (size_t)sendto(sock._fd, ptr, len, 0, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen); } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_posix_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_posix_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_posix_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_posix_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_posix_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_posix_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_posix_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_posix_write(sock, ptr, len, endpoint); } #endif /* defined(ZP_PLATFORM_SOCKET_POSIX) */ ================================================ FILE: src/link/transport/udp/udp_windows.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_WINDOWS) #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static WSADATA _z_udp_windows_wsa_data; static z_result_t _z_udp_windows_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_udp_windows_wsa_data) == 0) { ADDRINFOA hints; ep->_ep._iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_UDP; if (getaddrinfo(s_address, s_port, &hints, &ep->_ep._iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_windows_endpoint_clear(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_ep._iptcp); WSACleanup(); } static z_result_t _z_udp_windows_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; if (WSAStartup(MAKEWORD(2, 2), &_z_udp_windows_wsa_data) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } sock->_sock._fd = socket(endpoint._ep._iptcp->ai_family, endpoint._ep._iptcp->ai_socktype, endpoint._ep._iptcp->ai_protocol); if (sock->_sock._fd != INVALID_SOCKET) { DWORD tv = tout; if ((ret == _Z_RES_OK) && (setsockopt(sock->_sock._fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } if (ret != _Z_RES_OK) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_windows_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_windows_close(_z_sys_net_socket_t *sock) { if (sock->_sock._fd != INVALID_SOCKET) { closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; WSACleanup(); } } static size_t _z_udp_windows_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { SOCKADDR_STORAGE raddr; int addrlen = sizeof(raddr); int rb = recvfrom(sock._sock._fd, (char *)ptr, (int)len, 0, (SOCKADDR *)&raddr, &addrlen); if (rb == SOCKET_ERROR) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_windows_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_windows_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_windows_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { int wb = sendto(sock._sock._fd, (const char *)ptr, (int)len, 0, endpoint._ep._iptcp->ai_addr, (int)endpoint._ep._iptcp->ai_addrlen); if (wb == SOCKET_ERROR) { return SIZE_MAX; } return (size_t)wb; } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_windows_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_windows_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_windows_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_windows_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_windows_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_windows_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_windows_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_windows_write(sock, ptr, len, endpoint); } #endif /* defined(ZP_PLATFORM_SOCKET_WINDOWS) */ ================================================ FILE: src/link/transport/udp/udp_zephyr.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/udp_unicast.h" #if defined(ZP_PLATFORM_SOCKET_ZEPHYR) && (Z_FEATURE_LINK_UDP_UNICAST == 1) #include #include #include #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" static z_result_t _z_udp_zephyr_endpoint_init(_z_sys_net_endpoint_t *ep, const char *s_address, const char *s_port) { z_result_t ret = _Z_RES_OK; struct zsock_addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_UDP; if (getaddrinfo(s_address, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_udp_zephyr_endpoint_clear(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_iptcp); } static z_result_t _z_udp_zephyr_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_fd = socket(endpoint._iptcp->ai_family, endpoint._iptcp->ai_socktype, endpoint._iptcp->ai_protocol); if (sock->_fd != -1) { z_time_t tv; tv.tv_sec = tout / (uint32_t)1000; tv.tv_usec = (tout % (uint32_t)1000) * (uint32_t)1000; if ((ret == _Z_RES_OK) && (setsockopt(sock->_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) { /* Zephyr may reject this option depending on the network stack configuration. */ _Z_ERROR_LOG(_Z_ERR_GENERIC); } if (ret != _Z_RES_OK) { close(sock->_fd); sock->_fd = -1; } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_udp_zephyr_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { _ZP_UNUSED(sock); _ZP_UNUSED(endpoint); _ZP_UNUSED(tout); _Z_ERROR_LOG(_Z_ERR_GENERIC); return _Z_ERR_GENERIC; } static void _z_udp_zephyr_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } static size_t _z_udp_zephyr_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { struct sockaddr_storage raddr; unsigned int addrlen = sizeof(struct sockaddr_storage); ssize_t rb = recvfrom(sock._fd, ptr, len, 0, (struct sockaddr *)&raddr, &addrlen); if (rb < (ssize_t)0) { return SIZE_MAX; } return (size_t)rb; } static size_t _z_udp_zephyr_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_udp_zephyr_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_udp_zephyr_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return (size_t)sendto(sock._fd, ptr, len, 0, endpoint._iptcp->ai_addr, endpoint._iptcp->ai_addrlen); } z_result_t _z_udp_unicast_endpoint_init(_z_sys_net_endpoint_t *ep, const char *address, const char *port) { return _z_udp_zephyr_endpoint_init(ep, address, port); } void _z_udp_unicast_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_udp_zephyr_endpoint_clear(ep); } z_result_t _z_udp_unicast_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_zephyr_open(sock, endpoint, tout); } z_result_t _z_udp_unicast_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t endpoint, uint32_t tout) { return _z_udp_zephyr_listen(sock, endpoint, tout); } void _z_udp_unicast_close(_z_sys_net_socket_t *sock) { _z_udp_zephyr_close(sock); } size_t _z_udp_unicast_read(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_zephyr_read(sock, ptr, len); } size_t _z_udp_unicast_read_exact(_z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { return _z_udp_zephyr_read_exact(sock, ptr, len); } size_t _z_udp_unicast_write(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len, const _z_sys_net_endpoint_t endpoint) { return _z_udp_zephyr_write(sock, ptr, len, endpoint); } #endif ================================================ FILE: src/link/transport/upper/serial_protocol.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/serial_protocol.h" #include #include #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/link/config/serial.h" #include "zenoh-pico/link/transport/serial.h" #include "zenoh-pico/protocol/codec/serial.h" #include "zenoh-pico/protocol/definitions/serial.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_LINK_SERIAL == 1 #define SERIAL_CONNECT_THROTTLE_TIME_MS 250 typedef struct { bool _from_pins; uint32_t _baudrate; uint32_t _txpin; uint32_t _rxpin; char *_dev; } _z_serial_endpoint_cfg_t; static void _z_serial_endpoint_cfg_clear(_z_serial_endpoint_cfg_t *cfg) { z_free(cfg->_dev); cfg->_dev = NULL; } static z_result_t _z_serial_parse_u32(const char *str, uint32_t *value) { char *end = NULL; unsigned long parsed = 0; if (str == NULL || str[0] == '\0') { return _Z_ERR_CONFIG_LOCATOR_INVALID; } errno = 0; parsed = strtoul(str, &end, 10); if (errno != 0 || end == str || *end != '\0' || parsed == 0 || parsed > UINT32_MAX) { return _Z_ERR_CONFIG_LOCATOR_INVALID; } *value = (uint32_t)parsed; return _Z_RES_OK; } static char *_z_serial_copy_address(const _z_string_t *address) { size_t len = _z_string_len(address); char *copy = (char *)z_malloc(len + 1); if (copy != NULL) { _z_str_n_copy(copy, _z_string_data(address), len + 1); } return copy; } static z_result_t _z_serial_endpoint_parse(_z_serial_endpoint_cfg_t *cfg, const _z_endpoint_t *endpoint) { z_result_t ret = _Z_RES_OK; _z_string_t ser_str = _z_string_alias_str(SERIAL_SCHEMA); char *address = NULL; char *dot = NULL; const char *baudrate_str = NULL; (void)memset(cfg, 0, sizeof(*cfg)); if (!_z_string_equals(&endpoint->_locator._protocol, &ser_str)) { return _Z_ERR_CONFIG_LOCATOR_INVALID; } baudrate_str = _z_str_intmap_get(&endpoint->_config, SERIAL_CONFIG_BAUDRATE_KEY); ret = _z_serial_parse_u32(baudrate_str, &cfg->_baudrate); if (ret != _Z_RES_OK) { return ret; } address = _z_serial_copy_address(&endpoint->_locator._address); if (address == NULL || address[0] == '\0') { z_free(address); return _Z_ERR_CONFIG_LOCATOR_INVALID; } dot = strchr(address, '.'); if (dot == NULL) { cfg->_dev = address; return _Z_RES_OK; } *dot = '\0'; dot++; if (address[0] == '\0' || dot[0] == '\0') { z_free(address); return _Z_ERR_CONFIG_LOCATOR_INVALID; } ret = _z_serial_parse_u32(address, &cfg->_txpin); if (ret != _Z_RES_OK) { z_free(address); return ret; } ret = _z_serial_parse_u32(dot, &cfg->_rxpin); z_free(address); if (ret != _Z_RES_OK) { return ret; } cfg->_from_pins = true; return _Z_RES_OK; } static size_t _z_serial_write_all(_z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { size_t total = 0; while (total != len) { size_t wb = _z_serial_write(sock, _z_ptr_u8_offset((uint8_t *)ptr, (ptrdiff_t)total), len - total); if (wb == SIZE_MAX || wb == 0) { return SIZE_MAX; } total += wb; } return total; } static size_t _z_read_serial_internal(const _z_sys_net_socket_t sock, uint8_t *header, uint8_t *ptr, size_t len) { uint8_t *raw_buf = (uint8_t *)z_malloc(_Z_SERIAL_MAX_COBS_BUF_SIZE); if (raw_buf == NULL) { _Z_ERROR("Failed to allocate serial COBS buffer"); return SIZE_MAX; } size_t rb = 0; while (rb < _Z_SERIAL_MAX_COBS_BUF_SIZE) { size_t chunk = _z_serial_read(sock, &raw_buf[rb], 1); if (chunk != 1) { z_free(raw_buf); return SIZE_MAX; } rb++; if (raw_buf[rb - 1] == (uint8_t)0x00) { break; } } uint8_t *tmp_buf = (uint8_t *)z_malloc(_Z_SERIAL_MFS_SIZE); if (tmp_buf == NULL) { z_free(raw_buf); _Z_ERROR("Failed to allocate serial MFS buffer"); return SIZE_MAX; } size_t ret = _z_serial_msg_deserialize(raw_buf, rb, ptr, len, header, tmp_buf, _Z_SERIAL_MFS_SIZE); z_free(raw_buf); z_free(tmp_buf); return ret; } static size_t _z_send_serial_internal(const _z_sys_net_socket_t sock, uint8_t header, const uint8_t *ptr, size_t len) { uint8_t *tmp_buf = (uint8_t *)z_malloc(_Z_SERIAL_MFS_SIZE); uint8_t *raw_buf = (uint8_t *)z_malloc(_Z_SERIAL_MAX_COBS_BUF_SIZE); if ((raw_buf == NULL) || (tmp_buf == NULL)) { z_free(raw_buf); z_free(tmp_buf); _Z_ERROR("Failed to allocate serial COBS and/or MFS buffer"); return SIZE_MAX; } size_t raw_len = _z_serial_msg_serialize(raw_buf, _Z_SERIAL_MAX_COBS_BUF_SIZE, ptr, len, header, tmp_buf, _Z_SERIAL_MFS_SIZE); if (raw_len == SIZE_MAX) { z_free(raw_buf); z_free(tmp_buf); return SIZE_MAX; } size_t written = _z_serial_write_all(sock, raw_buf, raw_len); z_free(raw_buf); z_free(tmp_buf); return (written == raw_len) ? len : SIZE_MAX; } z_result_t _z_serial_endpoint_valid(const _z_endpoint_t *endpoint) { _z_serial_endpoint_cfg_t cfg; z_result_t ret = _z_serial_endpoint_parse(&cfg, endpoint); _z_serial_endpoint_cfg_clear(&cfg); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); } return ret; } static z_result_t _z_serial_open_impl(_z_serial_socket_t *sock, const _z_endpoint_t *endpoint, bool connect) { _z_serial_endpoint_cfg_t cfg; z_result_t ret = _Z_RES_OK; ret = _z_serial_endpoint_parse(&cfg, endpoint); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return ret; } if (cfg._from_pins) { ret = connect ? _z_serial_open_from_pins(&sock->_sock, cfg._txpin, cfg._rxpin, cfg._baudrate) : _z_serial_listen_from_pins(&sock->_sock, cfg._txpin, cfg._rxpin, cfg._baudrate); } else { ret = connect ? _z_serial_open_from_dev(&sock->_sock, cfg._dev, cfg._baudrate) : _z_serial_listen_from_dev(&sock->_sock, cfg._dev, cfg._baudrate); } if (ret != _Z_RES_OK || !connect) { _z_serial_endpoint_cfg_clear(&cfg); return ret; } ret = _z_connect_serial(sock->_sock); if (ret != _Z_RES_OK) { _z_serial_close(&sock->_sock); } _z_serial_endpoint_cfg_clear(&cfg); return ret; } z_result_t _z_serial_protocol_open(_z_serial_socket_t *sock, const _z_endpoint_t *endpoint) { return _z_serial_open_impl(sock, endpoint, true); } z_result_t _z_serial_protocol_listen(_z_serial_socket_t *sock, const _z_endpoint_t *endpoint) { return _z_serial_open_impl(sock, endpoint, false); } void _z_serial_protocol_close(_z_serial_socket_t *sock) { _z_serial_close(&sock->_sock); } z_result_t _z_connect_serial(const _z_sys_net_socket_t sock) { while (true) { uint8_t header = _Z_FLAG_SERIAL_INIT; _z_send_serial_internal(sock, header, NULL, 0); uint8_t tmp; size_t ret = _z_read_serial_internal(sock, &header, &tmp, sizeof(tmp)); if (ret == SIZE_MAX) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_RX_FAILED); } if (_Z_HAS_FLAG(header, _Z_FLAG_SERIAL_ACK) && _Z_HAS_FLAG(header, _Z_FLAG_SERIAL_INIT)) { _Z_DEBUG("connected"); break; } else if (_Z_HAS_FLAG(header, _Z_FLAG_SERIAL_RESET)) { z_sleep_ms(SERIAL_CONNECT_THROTTLE_TIME_MS); _Z_DEBUG("reset"); continue; } else { _Z_ERROR("unknown header received: %02X", header); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_RX_FAILED); } } return _Z_RES_OK; } size_t _z_read_serial(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { uint8_t header; return _z_read_serial_internal(sock, &header, ptr, len); } size_t _z_send_serial(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { return _z_send_serial_internal(sock, 0, ptr, len); } size_t _z_read_exact_serial(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; do { size_t rb = _z_read_serial(sock, ptr, len - n); if (rb == SIZE_MAX) { n = rb; break; } n += rb; } while (n != len); return n; } #endif /* Z_FEATURE_LINK_SERIAL == 1 */ ================================================ FILE: src/link/transport/upper/tls_stream.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tls_stream.h" #if Z_FEATURE_LINK_TLS == 1 #include #include #include #include #include #include #include #include "mbedtls/base64.h" #include "mbedtls/debug.h" #include "mbedtls/entropy.h" #include "mbedtls/error.h" #include "mbedtls/hmac_drbg.h" #include "mbedtls/md.h" #include "mbedtls/net_sockets.h" #include "mbedtls/pk.h" #include "mbedtls/ssl.h" #include "mbedtls/version.h" #include "mbedtls/x509.h" #include "mbedtls/x509_crt.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/config/tls.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #define Z_TLS_BASE64_MAX_VALUE_LEN (64 * 1024) #ifdef ZENOH_LOG_TRACE static void _z_tls_debug(void *ctx, int level, const char *file, int line, const char *str) { _ZP_UNUSED(ctx); _Z_DEBUG_NONL("mbed TLS [%d] %s:%04d: %s", level, file, line, str); } #endif static _z_tls_context_t *_z_tls_context_new(void); static void _z_tls_context_free(_z_tls_context_t **ctx); static z_result_t _z_tls_decode_base64(const char *label, const char *input, unsigned char **output, size_t *output_len) { if (input == NULL) { return _Z_ERR_GENERIC; } if (label == NULL) { _Z_ERROR("TLS base64 value label is NULL"); return _Z_ERR_GENERIC; } size_t input_len = strnlen(input, Z_TLS_BASE64_MAX_VALUE_LEN + 1); if (input_len > Z_TLS_BASE64_MAX_VALUE_LEN) { _Z_ERROR("%s exceeds maximum supported value length (%zu bytes)", label, (size_t)Z_TLS_BASE64_MAX_VALUE_LEN); return _Z_ERR_GENERIC; } size_t required = 0; int ret = mbedtls_base64_decode(NULL, 0, &required, (const unsigned char *)input, input_len); if (ret != 0 && ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { _Z_ERROR("Failed to decode %s from base64: -0x%04x", label, -ret); return _Z_ERR_GENERIC; } size_t buffer_len = (required > 0) ? required : 1; unsigned char *buffer = (unsigned char *)z_malloc(buffer_len + 1); if (buffer == NULL) { _Z_ERROR("Failed to allocate buffer for %s base64 (%zu bytes)", label, buffer_len); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } ret = mbedtls_base64_decode(buffer, buffer_len, &required, (const unsigned char *)input, input_len); if (ret != 0) { _Z_ERROR("Failed to decode %s from base64: -0x%04x", label, -ret); z_free(buffer); return _Z_ERR_GENERIC; } buffer[required] = '\0'; *output = buffer; if (output_len != NULL) { *output_len = required; } return _Z_RES_OK; } static z_result_t _z_tls_parse_cert_from_base64(mbedtls_x509_crt *cert, const char *base64, const char *label) { unsigned char *decoded = NULL; size_t decoded_len = 0; z_result_t res = _z_tls_decode_base64(label, base64, &decoded, &decoded_len); if (res != _Z_RES_OK) { return res; } int ret = mbedtls_x509_crt_parse(cert, decoded, decoded_len + 1); z_free(decoded); if (ret != 0) { _Z_ERROR("Failed to parse %s from base64: -0x%04x", label, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded %s from base64", label); return _Z_RES_OK; } static z_result_t _z_tls_parse_key_from_base64(mbedtls_pk_context *key, const char *base64, const char *label, mbedtls_hmac_drbg_context *rng) { unsigned char *decoded = NULL; size_t decoded_len = 0; z_result_t res = _z_tls_decode_base64(label, base64, &decoded, &decoded_len); if (res != _Z_RES_OK) { return res; } #if MBEDTLS_VERSION_MAJOR >= 3 int ret = mbedtls_pk_parse_key(key, decoded, decoded_len + 1, NULL, 0, mbedtls_hmac_drbg_random, rng); #else _ZP_UNUSED(rng); int ret = mbedtls_pk_parse_key(key, decoded, decoded_len + 1, NULL, 0); #endif z_free(decoded); if (ret != 0) { _Z_ERROR("Failed to parse %s from base64: -0x%04x", label, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded %s from base64", label); return _Z_RES_OK; } static bool _z_opt_is_true(const char *val) { if (val == NULL || val[0] == '\0') { return true; } char c = val[0]; return !(c == '0' || c == 'n' || c == 'N' || c == 'f' || c == 'F'); } static int _z_tls_bio_send(void *ctx, const unsigned char *buf, size_t len) { int fd = *(int *)ctx; ssize_t n = send(fd, buf, len, MSG_NOSIGNAL); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return MBEDTLS_ERR_SSL_WANT_WRITE; } return MBEDTLS_ERR_NET_SEND_FAILED; } return (int)n; } static int _z_tls_bio_recv(void *ctx, unsigned char *buf, size_t len) { int fd = *(int *)ctx; ssize_t n = recv(fd, buf, len, 0); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return MBEDTLS_ERR_SSL_WANT_READ; } return MBEDTLS_ERR_NET_RECV_FAILED; } if (n == 0) { return 0; } return (int)n; } static _z_tls_context_t *_z_tls_context_new(void) { _z_tls_context_t *ctx = (_z_tls_context_t *)z_malloc(sizeof(_z_tls_context_t)); if (ctx == NULL) { return NULL; } mbedtls_ssl_init(&ctx->_ssl); mbedtls_ssl_config_init(&ctx->_ssl_config); mbedtls_entropy_init(&ctx->_entropy); mbedtls_hmac_drbg_init(&ctx->_hmac_drbg); mbedtls_x509_crt_init(&ctx->_ca_cert); mbedtls_pk_init(&ctx->_listen_key); mbedtls_x509_crt_init(&ctx->_listen_cert); mbedtls_pk_init(&ctx->_client_key); mbedtls_x509_crt_init(&ctx->_client_cert); ctx->_enable_mtls = false; #ifdef ZENOH_LOG_TRACE mbedtls_debug_set_threshold(4); mbedtls_ssl_conf_dbg(&ctx->_ssl_config, _z_tls_debug, NULL); #endif int ret = mbedtls_hmac_drbg_seed(&ctx->_hmac_drbg, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), mbedtls_entropy_func, &ctx->_entropy, NULL, 0); if (ret != 0) { _Z_ERROR("Failed to seed HMAC_DRBG: -0x%04x", -ret); _z_tls_context_free(&ctx); return NULL; } return ctx; } static void _z_tls_context_free(_z_tls_context_t **ctx) { if (ctx != NULL && *ctx != NULL) { mbedtls_ssl_free(&(*ctx)->_ssl); mbedtls_ssl_config_free(&(*ctx)->_ssl_config); mbedtls_entropy_free(&(*ctx)->_entropy); mbedtls_hmac_drbg_free(&(*ctx)->_hmac_drbg); mbedtls_x509_crt_free(&(*ctx)->_ca_cert); mbedtls_pk_free(&(*ctx)->_listen_key); mbedtls_x509_crt_free(&(*ctx)->_listen_cert); mbedtls_pk_free(&(*ctx)->_client_key); mbedtls_x509_crt_free(&(*ctx)->_client_cert); z_free(*ctx); *ctx = NULL; } } static z_result_t _z_tls_load_ca_certificate(_z_tls_context_t *ctx, const _z_str_intmap_t *config) { const char *ca_cert_str = _z_str_intmap_get(config, TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY); const char *ca_cert_base64 = _z_str_intmap_get(config, TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_KEY); if (ca_cert_str == NULL && ca_cert_base64 == NULL) { _Z_ERROR("TLS requires 'root_ca_certificate' to be set"); return _Z_ERR_GENERIC; } if (ca_cert_str != NULL) { int ret = mbedtls_x509_crt_parse_file(&ctx->_ca_cert, ca_cert_str); if (ret != 0) { _Z_ERROR("Failed to parse CA certificate file %s: -0x%04x", ca_cert_str, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded CA certificate from %s", ca_cert_str); } if (ca_cert_base64 != NULL) { z_result_t res = _z_tls_parse_cert_from_base64(&ctx->_ca_cert, ca_cert_base64, "CA certificate"); if (res != _Z_RES_OK) { return res; } } return _Z_RES_OK; } static z_result_t _z_tls_load_listen_cert(_z_tls_context_t *ctx, const _z_str_intmap_t *config) { const char *listen_key_str = _z_str_intmap_get(config, TLS_CONFIG_LISTEN_PRIVATE_KEY_KEY); const char *listen_key_base64 = _z_str_intmap_get(config, TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_KEY); const char *listen_cert_str = _z_str_intmap_get(config, TLS_CONFIG_LISTEN_CERTIFICATE_KEY); const char *listen_cert_base64 = _z_str_intmap_get(config, TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_KEY); if ((listen_key_str == NULL && listen_key_base64 == NULL) || (listen_cert_str == NULL && listen_cert_base64 == NULL)) { _Z_ERROR("TLS server requires both private key and certificate to be configured"); return _Z_ERR_GENERIC; } if (listen_key_base64 != NULL) { z_result_t res = _z_tls_parse_key_from_base64(&ctx->_listen_key, listen_key_base64, "listening private key", &ctx->_hmac_drbg); if (res != _Z_RES_OK) { return res; } } else { #if MBEDTLS_VERSION_MAJOR >= 3 int ret = mbedtls_pk_parse_keyfile(&ctx->_listen_key, listen_key_str, NULL, mbedtls_hmac_drbg_random, &ctx->_hmac_drbg); #else int ret = mbedtls_pk_parse_keyfile(&ctx->_listen_key, listen_key_str, NULL); #endif if (ret != 0) { _Z_ERROR("Failed to parse listening side private key file %s: -0x%04x", listen_key_str, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded listening private key from %s", listen_key_str); } if (listen_cert_base64 != NULL) { z_result_t res = _z_tls_parse_cert_from_base64(&ctx->_listen_cert, listen_cert_base64, "listening side certificate"); if (res != _Z_RES_OK) { return res; } } else { int ret = mbedtls_x509_crt_parse_file(&ctx->_listen_cert, listen_cert_str); if (ret != 0) { _Z_ERROR("Failed to parse listening side certificate file %s: -0x%04x", listen_cert_str, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded listening side certificate from %s", listen_cert_str); } return _Z_RES_OK; } static z_result_t _z_tls_load_client_cert(_z_tls_context_t *ctx, const _z_str_intmap_t *config) { const char *key_path = _z_str_intmap_get(config, TLS_CONFIG_CONNECT_PRIVATE_KEY_KEY); const char *key_base64 = _z_str_intmap_get(config, TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_KEY); const char *cert_path = _z_str_intmap_get(config, TLS_CONFIG_CONNECT_CERTIFICATE_KEY); const char *cert_base64 = _z_str_intmap_get(config, TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_KEY); if ((key_path == NULL && key_base64 == NULL) || (cert_path == NULL && cert_base64 == NULL)) { _Z_ERROR("mTLS requires both client private key and certificate"); return _Z_ERR_GENERIC; } if (key_base64 != NULL) { z_result_t res = _z_tls_parse_key_from_base64(&ctx->_client_key, key_base64, "client private key", &ctx->_hmac_drbg); if (res != _Z_RES_OK) { return res; } } else { #if MBEDTLS_VERSION_MAJOR >= 3 int ret = mbedtls_pk_parse_keyfile(&ctx->_client_key, key_path, NULL, mbedtls_hmac_drbg_random, &ctx->_hmac_drbg); #else int ret = mbedtls_pk_parse_keyfile(&ctx->_client_key, key_path, NULL); #endif if (ret != 0) { _Z_ERROR("Failed to parse client private key file %s: -0x%04x", key_path, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded client private key from %s", key_path); } if (cert_base64 != NULL) { z_result_t res = _z_tls_parse_cert_from_base64(&ctx->_client_cert, cert_base64, "client certificate"); if (res != _Z_RES_OK) { return res; } } else { int ret = mbedtls_x509_crt_parse_file(&ctx->_client_cert, cert_path); if (ret != 0) { _Z_ERROR("Failed to parse client certificate file %s: -0x%04x", cert_path, -ret); return _Z_ERR_GENERIC; } _Z_DEBUG("Loaded client certificate from %s", cert_path); } return _Z_RES_OK; } z_result_t _z_open_tls(_z_tls_socket_t *sock, const _z_sys_net_endpoint_t *rep, const char *hostname, const _z_str_intmap_t *config, bool peer_socket) { if ((rep == NULL) || (rep->_iptcp == NULL)) { _Z_ERROR("Invalid TCP endpoint for TLS connection"); return _Z_ERR_GENERIC; } sock->_is_peer_socket = peer_socket; bool verify_name = true; const char *verify_opt = _z_str_intmap_get(config, TLS_CONFIG_VERIFY_NAME_ON_CONNECT_KEY); if (verify_opt != NULL && !_z_opt_is_true(verify_opt)) { verify_name = false; } bool enable_mtls = false; const char *mtls_opt = _z_str_intmap_get(config, TLS_CONFIG_ENABLE_MTLS_KEY); if (mtls_opt != NULL && _z_opt_is_true(mtls_opt)) { enable_mtls = true; } sock->_tls_ctx = _z_tls_context_new(); if (sock->_tls_ctx == NULL) { _Z_ERROR("Failed to create TLS context"); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (enable_mtls) { z_result_t ret_client = _z_tls_load_client_cert(sock->_tls_ctx, config); if (ret_client != _Z_RES_OK) { _z_tls_context_free(&sock->_tls_ctx); return ret_client; } } sock->_tls_ctx->_enable_mtls = enable_mtls; z_result_t ret = _z_tls_load_ca_certificate(sock->_tls_ctx, config); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to load CA certificate"); _z_tls_context_free(&sock->_tls_ctx); return ret; } ret = _z_tcp_open(&sock->_sock, *rep, Z_CONFIG_SOCKET_TIMEOUT); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to open lower TCP socket: %d", ret); _z_tls_context_free(&sock->_tls_ctx); return ret; } // Needed for _read_socket_f callback which requires TLS context sock->_sock._tls_sock = (void *)sock; int mbedret = mbedtls_ssl_config_defaults(&sock->_tls_ctx->_ssl_config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (mbedret != 0) { _Z_ERROR("Failed to set SSL config defaults: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } if (sock->_tls_ctx->_ca_cert.version != 0) { mbedtls_ssl_conf_ca_chain(&sock->_tls_ctx->_ssl_config, &sock->_tls_ctx->_ca_cert, NULL); } mbedtls_ssl_conf_authmode(&sock->_tls_ctx->_ssl_config, verify_name ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_rng(&sock->_tls_ctx->_ssl_config, mbedtls_hmac_drbg_random, &sock->_tls_ctx->_hmac_drbg); if (enable_mtls) { int own_ret = mbedtls_ssl_conf_own_cert(&sock->_tls_ctx->_ssl_config, &sock->_tls_ctx->_client_cert, &sock->_tls_ctx->_client_key); if (own_ret != 0) { _Z_ERROR("Failed to configure client certificate: -0x%04x", -own_ret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } } mbedret = mbedtls_ssl_setup(&sock->_tls_ctx->_ssl, &sock->_tls_ctx->_ssl_config); if (mbedret != 0) { _Z_ERROR("Failed to setup SSL: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } if (!hostname) { _Z_ERROR("No hostname is set"); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } mbedret = mbedtls_ssl_set_hostname(&sock->_tls_ctx->_ssl, hostname); if (mbedret != 0) { _Z_ERROR("Failed to set hostname: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } mbedtls_ssl_set_bio(&sock->_tls_ctx->_ssl, &sock->_sock._fd, _z_tls_bio_send, _z_tls_bio_recv, NULL); while ((mbedret = mbedtls_ssl_handshake(&sock->_tls_ctx->_ssl)) != 0) { if (mbedret != MBEDTLS_ERR_SSL_WANT_READ && mbedret != MBEDTLS_ERR_SSL_WANT_WRITE) { _Z_ERROR("TLS handshake failed: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } } uint32_t ignored_flags = verify_name ? 0u : MBEDTLS_X509_BADCERT_CN_MISMATCH; uint32_t verify_result = mbedtls_ssl_get_verify_result(&sock->_tls_ctx->_ssl); if (verify_result != 0) { if ((verify_result & ~ignored_flags) != 0u) { _Z_ERROR("TLS client certificate verification failed: 0x%08x", verify_result); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } if (!verify_name) { _Z_INFO("TLS client name verification disabled; ignoring certificate name mismatch"); } } return _Z_RES_OK; } z_result_t _z_listen_tls(_z_tls_socket_t *sock, const _z_sys_net_endpoint_t *rep, const _z_str_intmap_t *config) { if ((rep == NULL) || (rep->_iptcp == NULL)) { _Z_ERROR("Invalid lower TCP endpoint for TLS listen"); return _Z_ERR_GENERIC; } sock->_is_peer_socket = false; bool enable_mtls = false; const char *mtls_opt = _z_str_intmap_get(config, TLS_CONFIG_ENABLE_MTLS_KEY); if (mtls_opt != NULL && _z_opt_is_true(mtls_opt)) { enable_mtls = true; } sock->_tls_ctx = _z_tls_context_new(); if (sock->_tls_ctx == NULL) { _Z_ERROR("Failed to create TLS context"); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } z_result_t ret = _z_tls_load_ca_certificate(sock->_tls_ctx, config); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to load CA certificate"); _z_tls_context_free(&sock->_tls_ctx); return ret; } ret = _z_tls_load_listen_cert(sock->_tls_ctx, config); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to load listening side certificate"); _z_tls_context_free(&sock->_tls_ctx); return ret; } sock->_tls_ctx->_enable_mtls = enable_mtls; ret = _z_tcp_listen(&sock->_sock, *rep); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to listen on lower TCP socket for TLS, ret=%d", ret); _z_tls_context_free(&sock->_tls_ctx); return ret; } // Needed for _read_socket_f callback which requires TLS context sock->_sock._tls_sock = (void *)sock; int mbedret = mbedtls_ssl_config_defaults(&sock->_tls_ctx->_ssl_config, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (mbedret != 0) { _Z_ERROR("Failed to set SSL config defaults for server: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } if (sock->_tls_ctx->_ca_cert.version != 0) { mbedtls_ssl_conf_ca_chain(&sock->_tls_ctx->_ssl_config, &sock->_tls_ctx->_ca_cert, NULL); } if (sock->_tls_ctx->_listen_cert.version != 0) { mbedret = mbedtls_ssl_conf_own_cert(&sock->_tls_ctx->_ssl_config, &sock->_tls_ctx->_listen_cert, &sock->_tls_ctx->_listen_key); if (mbedret != 0) { _Z_ERROR("Failed to configure server certificate: -0x%04x", -mbedret); _z_tcp_close(&sock->_sock); _z_tls_context_free(&sock->_tls_ctx); return _Z_ERR_GENERIC; } } mbedtls_ssl_conf_authmode(&sock->_tls_ctx->_ssl_config, enable_mtls ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_rng(&sock->_tls_ctx->_ssl_config, mbedtls_hmac_drbg_random, &sock->_tls_ctx->_hmac_drbg); return _Z_RES_OK; } z_result_t _z_tls_accept(_z_sys_net_socket_t *socket, const _z_sys_net_socket_t *listen_sock) { socket->_tls_sock = z_malloc(sizeof(_z_tls_socket_t)); if (socket->_tls_sock == NULL) { _Z_ERROR("Failed to allocate TLS socket structure"); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _z_tls_socket_t *tls_sock = (_z_tls_socket_t *)socket->_tls_sock; tls_sock->_tls_ctx = _z_tls_context_new(); if (tls_sock->_tls_ctx == NULL) { _Z_ERROR("Failed to create TLS context"); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (listen_sock == NULL) { _Z_ERROR("Listening TLS socket is NULL"); _z_tls_context_free(&tls_sock->_tls_ctx); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_GENERIC; } tls_sock->_sock = *socket; mbedtls_ssl_init(&tls_sock->_tls_ctx->_ssl); // Setup SSL context using the listen socket's configuration _z_tls_socket_t *listen_tls_sock = (_z_tls_socket_t *)listen_sock->_tls_sock; if (listen_tls_sock == NULL || listen_tls_sock->_tls_ctx == NULL) { _Z_ERROR("Listening TLS socket's TLS context is NULL"); _z_tls_context_free(&tls_sock->_tls_ctx); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_GENERIC; } tls_sock->_tls_ctx->_enable_mtls = listen_tls_sock->_tls_ctx->_enable_mtls; mbedtls_ssl_config *listen_conf = &listen_tls_sock->_tls_ctx->_ssl_config; int mbedret = mbedtls_ssl_setup(&tls_sock->_tls_ctx->_ssl, listen_conf); if (mbedret != 0) { _Z_ERROR("Failed to setup SSL: -0x%04x", -mbedret); _z_tls_context_free(&tls_sock->_tls_ctx); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_GENERIC; } mbedtls_ssl_set_bio(&tls_sock->_tls_ctx->_ssl, &tls_sock->_sock._fd, _z_tls_bio_send, _z_tls_bio_recv, NULL); while ((mbedret = mbedtls_ssl_handshake(&tls_sock->_tls_ctx->_ssl)) != 0) { if (mbedret != MBEDTLS_ERR_SSL_WANT_READ && mbedret != MBEDTLS_ERR_SSL_WANT_WRITE) { _Z_ERROR("TLS server handshake failed: -0x%04x", -mbedret); _z_tls_context_free(&tls_sock->_tls_ctx); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_GENERIC; } } uint32_t verify_result = mbedtls_ssl_get_verify_result(&tls_sock->_tls_ctx->_ssl); if (verify_result != 0) { uint32_t allowed_flags = 0u; if (!tls_sock->_tls_ctx->_enable_mtls) { allowed_flags |= MBEDTLS_X509_BADCERT_SKIP_VERIFY; } if ((verify_result & ~allowed_flags) != 0u) { _Z_ERROR("TLS client certificate verification failed: 0x%08x", verify_result); _z_tls_context_free(&tls_sock->_tls_ctx); z_free(socket->_tls_sock); socket->_tls_sock = NULL; return _Z_ERR_GENERIC; } } tls_sock->_is_peer_socket = true; tls_sock->_sock._tls_sock = (void *)tls_sock; socket->_fd = tls_sock->_sock._fd; socket->_tls_sock = (void *)tls_sock; return _Z_RES_OK; } void _z_close_tls_socket(_z_sys_net_socket_t *socket) { if (socket == NULL) { return; } if (socket->_tls_sock == NULL) { return; } _z_tls_socket_t *tls_sock = (_z_tls_socket_t *)socket->_tls_sock; bool peer_socket = tls_sock->_is_peer_socket; _z_close_tls(tls_sock); if (peer_socket) { z_free(tls_sock); } socket->_tls_sock = NULL; socket->_fd = -1; } void _z_close_tls(_z_tls_socket_t *sock) { if (sock->_tls_ctx != NULL) { mbedtls_ssl_close_notify(&sock->_tls_ctx->_ssl); _z_tls_context_free(&sock->_tls_ctx); } _z_tcp_close(&sock->_sock); sock->_sock._tls_sock = NULL; } size_t _z_read_tls(const _z_tls_socket_t *sock, uint8_t *ptr, size_t len) { if (sock->_tls_ctx == NULL) { _Z_ERROR("TLS context is NULL"); return SIZE_MAX; } int ret = mbedtls_ssl_read(&sock->_tls_ctx->_ssl, ptr, len); if (ret > 0) { return (size_t)ret; } if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { return 0; } if (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || ret == MBEDTLS_ERR_SSL_CONN_EOF) { return SIZE_MAX; } _Z_ERROR("TLS read error: %d", ret); return SIZE_MAX; } size_t _z_write_tls(const _z_tls_socket_t *sock, const uint8_t *ptr, size_t len) { if (sock->_tls_ctx == NULL) { _Z_ERROR("TLS context is NULL"); return SIZE_MAX; } int ret = mbedtls_ssl_write(&sock->_tls_ctx->_ssl, ptr, len); if (ret > 0) { return (size_t)ret; } if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { return 0; } _Z_ERROR("TLS write error: -0x%04x", -ret); return SIZE_MAX; } size_t _z_write_all_tls(const _z_tls_socket_t *sock, const uint8_t *ptr, size_t len) { size_t n = 0; do { size_t wb = _z_write_tls(sock, &ptr[n], len - n); if (wb == SIZE_MAX) { return wb; } n += wb; } while (n < len); return n; } #endif // Z_FEATURE_LINK_TLS == 1 ================================================ FILE: src/link/transport/upper/ws_emscripten.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_EMSCRIPTEN) && Z_FEATURE_LINK_WS == 1 #include #include #include #include #include #include #include #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/ws.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #define WS_LINK_SLEEP 1 static z_result_t _z_ws_emscripten_create_endpoint(_z_sys_net_endpoint_t *ep, const char *s_addr, const char *s_port) { z_result_t ret = _Z_RES_OK; struct addrinfo hints; ep->_iptcp = NULL; (void)memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = 0; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(s_addr, s_port, &hints, &ep->_iptcp) != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static void _z_ws_emscripten_free_endpoint(_z_sys_net_endpoint_t *ep) { freeaddrinfo(ep->_iptcp); } static z_result_t _z_ws_emscripten_open(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t rep, uint32_t tout) { z_result_t ret = _Z_RES_OK; sock->_ws._fd = socket(rep._iptcp->ai_family, rep._iptcp->ai_socktype, rep._iptcp->ai_protocol); if (sock->_ws._fd != -1) { // WARNING: commented because setsockopt is not implemented in emscripten // if ((ret == _Z_RES_OK) && (setsockopt(sock->_ws._fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) < 0)) // { // ret = _Z_ERR_GENERIC; // } sock->_ws._tout = tout; // We are storing the timeout that we are going to use when sending and receiving struct addrinfo *it = NULL; for (it = rep._iptcp; it != NULL; it = it->ai_next) { if ((ret == _Z_RES_OK) && (connect(sock->_ws._fd, it->ai_addr, it->ai_addrlen) < 0)) { break; // WARNING: breaking here because connect returns -1 even if the // underlying socket is actually open. // if (it->ai_next == NULL) { // ret = _Z_ERR_GENERIC; // break; // } } else { break; } } // WARNING: workaround as connect returns before the websocket is // actually open. z_sleep_ms(100); if (ret != _Z_RES_OK) { close(sock->_ws._fd); } } else { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } static z_result_t _z_ws_emscripten_listen(_z_sys_net_socket_t *sock, const _z_sys_net_endpoint_t lep) { z_result_t ret = _Z_RES_OK; _ZP_UNUSED(sock); _ZP_UNUSED(lep); // @TODO: To be implemented _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; return ret; } static void _z_ws_emscripten_close(_z_sys_net_socket_t *sock) { close(sock->_ws._fd); } static size_t _z_ws_emscripten_read(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { // WARNING: workaroud implementing here the timeout ssize_t rb = 0; z_time_t start = z_time_now(); while ((z_time_elapsed_ms(&start) < sock._ws._tout) && (rb <= 0)) { z_sleep_ms(WS_LINK_SLEEP); rb = recv(sock._ws._fd, ptr, len, 0); } if (rb < 0) { rb = SIZE_MAX; } return (size_t)rb; } static size_t _z_ws_emscripten_read_exact(const _z_sys_net_socket_t sock, uint8_t *ptr, size_t len) { size_t n = 0; uint8_t *pos = &ptr[0]; do { size_t rb = _z_ws_emscripten_read(sock, pos, len - n); if ((rb == SIZE_MAX) || (rb == 0)) { n = rb; break; } n = n + rb; pos = _z_ptr_u8_offset(pos, rb); } while (n != len); return n; } static size_t _z_ws_emscripten_write(const _z_sys_net_socket_t sock, const uint8_t *ptr, size_t len) { // WARNING: workaroud implementing here the timeout ssize_t sb = 0; z_time_t start = z_time_now(); while ((z_time_elapsed_ms(&start) < sock._ws._tout) && (sb <= 0)) { z_sleep_ms(WS_LINK_SLEEP); sb = send(sock._ws._fd, ptr, len, 0); } // WARNING: workaround as the recv returns -1 not only in case of errors if (sb < 0) { sb = 0; } return (size_t)sb; } z_result_t _z_ws_endpoint_init(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { z_result_t ret = _Z_RES_OK; char *host = _z_endpoint_parse_host(address); char *port = _z_endpoint_parse_port(address); if ((host == NULL) || (port == NULL)) { ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } else { ret = _z_ws_emscripten_create_endpoint(ep, host, port); } z_free(host); z_free(port); return ret; } void _z_ws_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_ws_emscripten_free_endpoint(ep); } z_result_t _z_ws_transport_open(_z_ws_socket_t *sock, uint32_t tout) { return _z_ws_emscripten_open(&sock->_sock, sock->_rep, tout); } z_result_t _z_ws_transport_listen(_z_ws_socket_t *sock) { return _z_ws_emscripten_listen(&sock->_sock, sock->_rep); } void _z_ws_transport_close(_z_ws_socket_t *sock) { _z_ws_emscripten_close(&sock->_sock); } size_t _z_ws_transport_read(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len) { return _z_ws_emscripten_read(sock->_sock, ptr, len); } size_t _z_ws_transport_read_exact(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len) { return _z_ws_emscripten_read_exact(sock->_sock, ptr, len); } size_t _z_ws_transport_write(const _z_ws_socket_t *sock, const uint8_t *ptr, size_t len) { return _z_ws_emscripten_write(sock->_sock, ptr, len); } size_t _z_ws_transport_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_ws_emscripten_read(socket, ptr, len); } #else typedef int _zp_ws_emscripten_transport_disabled_t; #endif ================================================ FILE: src/link/transport/upper/ws_stream.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/link/transport/ws.h" #if Z_FEATURE_LINK_WS == 1 z_result_t _z_ws_endpoint_init(_z_sys_net_endpoint_t *ep, const _z_string_t *address) { return _z_tcp_endpoint_init_from_address(ep, address); } void _z_ws_endpoint_clear(_z_sys_net_endpoint_t *ep) { _z_tcp_endpoint_clear(ep); } z_result_t _z_ws_transport_open(_z_ws_socket_t *sock, uint32_t tout) { return _z_tcp_open(&sock->_sock, sock->_rep, tout); } z_result_t _z_ws_transport_listen(_z_ws_socket_t *sock) { return _z_tcp_listen(&sock->_sock, sock->_rep); } void _z_ws_transport_close(_z_ws_socket_t *sock) { _z_tcp_close(&sock->_sock); } size_t _z_ws_transport_read(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len) { return _z_tcp_read(sock->_sock, ptr, len); } size_t _z_ws_transport_read_exact(const _z_ws_socket_t *sock, uint8_t *ptr, size_t len) { return _z_tcp_read_exact(sock->_sock, ptr, len); } size_t _z_ws_transport_write(const _z_ws_socket_t *sock, const uint8_t *ptr, size_t len) { return _z_tcp_write(sock->_sock, ptr, len); } size_t _z_ws_transport_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_tcp_read(socket, ptr, len); } #endif ================================================ FILE: src/link/unicast/serial.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/serial_protocol.h" #if Z_FEATURE_LINK_SERIAL == 1 z_result_t _z_endpoint_serial_valid(_z_endpoint_t *endpoint) { return _z_serial_endpoint_valid(endpoint); } z_result_t _z_f_link_open_serial(_z_link_t *self) { return _z_serial_protocol_open(&self->_socket._serial, &self->_endpoint); } z_result_t _z_f_link_listen_serial(_z_link_t *self) { return _z_serial_protocol_listen(&self->_socket._serial, &self->_endpoint); } void _z_f_link_close_serial(_z_link_t *self) { _z_serial_protocol_close(&self->_socket._serial); } void _z_f_link_free_serial(_z_link_t *self) { (void)(self); } size_t _z_f_link_write_serial(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); return _z_send_serial(self->_socket._serial._sock, ptr, len); } size_t _z_f_link_write_all_serial(const _z_link_t *self, const uint8_t *ptr, size_t len) { return _z_send_serial(self->_socket._serial._sock, ptr, len); } size_t _z_f_link_read_serial(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(addr); return _z_read_serial(self->_socket._serial._sock, ptr, len); } size_t _z_f_link_read_exact_serial(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(addr); _ZP_UNUSED(socket); return _z_read_exact_serial(self->_socket._serial._sock, ptr, len); } size_t _z_f_link_read_socket_serial(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_read_serial(socket, ptr, len); } uint16_t _z_get_link_mtu_serial(void) { return _Z_SERIAL_MTU_SIZE; } z_result_t _z_new_link_serial(_z_link_t *zl, _z_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; zl->_type = _Z_LINK_TYPE_SERIAL; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_UNICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_DATAGRAM; zl->_cap._is_reliable = false; zl->_mtu = _z_get_link_mtu_serial(); zl->_endpoint = endpoint; zl->_open_f = _z_f_link_open_serial; zl->_listen_f = _z_f_link_listen_serial; zl->_close_f = _z_f_link_close_serial; zl->_free_f = _z_f_link_free_serial; zl->_write_f = _z_f_link_write_serial; zl->_write_all_f = _z_f_link_write_all_serial; zl->_read_f = _z_f_link_read_serial; zl->_read_exact_f = _z_f_link_read_exact_serial; zl->_read_socket_f = _z_f_link_read_socket_serial; return ret; } #endif ================================================ FILE: src/link/unicast/tcp.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/tcp.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/tcp.h" #if Z_FEATURE_LINK_TCP == 1 z_result_t _z_endpoint_tcp_valid(_z_endpoint_t *endpoint) { _z_string_t tcp_str = _z_string_alias_str(TCP_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &tcp_str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _z_tcp_address_valid(&endpoint->_locator._address); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); } return ret; } z_result_t _z_f_link_open_tcp(_z_link_t *zl) { uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&zl->_endpoint._config, TCP_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } return _z_tcp_open(&zl->_socket._tcp._sock, zl->_socket._tcp._rep, tout); } z_result_t _z_f_link_listen_tcp(_z_link_t *zl) { return _z_tcp_listen(&zl->_socket._tcp._sock, zl->_socket._tcp._rep); } void _z_f_link_close_tcp(_z_link_t *zl) { _z_tcp_close(&zl->_socket._tcp._sock); } void _z_f_link_free_tcp(_z_link_t *zl) { _z_tcp_endpoint_clear(&zl->_socket._tcp._rep); } size_t _z_f_link_write_tcp(const _z_link_t *zl, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { if (socket != NULL) { return _z_tcp_write(*socket, ptr, len); } else { return _z_tcp_write(zl->_socket._tcp._sock, ptr, len); } } size_t _z_f_link_write_all_tcp(const _z_link_t *zl, const uint8_t *ptr, size_t len) { return _z_tcp_write(zl->_socket._tcp._sock, ptr, len); } size_t _z_f_link_read_tcp(const _z_link_t *zl, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(addr); return _z_tcp_read(zl->_socket._tcp._sock, ptr, len); } size_t _z_f_link_read_exact_tcp(const _z_link_t *zl, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(addr); if (socket != NULL) { return _z_tcp_read_exact(*socket, ptr, len); } else { return _z_tcp_read_exact(zl->_socket._tcp._sock, ptr, len); } } size_t _z_f_link_tcp_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_tcp_read(socket, ptr, len); } uint16_t _z_get_link_mtu_tcp(void) { // Maximum MTU for TCP return 65535; } z_result_t _z_new_peer_tcp(_z_endpoint_t *endpoint, _z_sys_net_socket_t *socket) { _z_sys_net_endpoint_t sys_endpoint = {0}; z_result_t ret = _z_tcp_endpoint_init_from_address(&sys_endpoint, &endpoint->_locator._address); if (ret != _Z_RES_OK) { _z_tcp_endpoint_clear(&sys_endpoint); return ret; } ret = _z_tcp_open(socket, sys_endpoint, Z_CONFIG_SOCKET_TIMEOUT); _z_tcp_endpoint_clear(&sys_endpoint); return ret; } z_result_t _z_new_link_tcp(_z_link_t *zl, _z_endpoint_t *endpoint) { zl->_type = _Z_LINK_TYPE_TCP; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_UNICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_STREAM; zl->_cap._is_reliable = true; zl->_mtu = _z_get_link_mtu_tcp(); zl->_endpoint = *endpoint; z_result_t ret = _z_tcp_endpoint_init_from_address(&zl->_socket._tcp._rep, &endpoint->_locator._address); zl->_open_f = _z_f_link_open_tcp; zl->_listen_f = _z_f_link_listen_tcp; zl->_close_f = _z_f_link_close_tcp; zl->_free_f = _z_f_link_free_tcp; zl->_write_f = _z_f_link_write_tcp; zl->_write_all_f = _z_f_link_write_all_tcp; zl->_read_f = _z_f_link_read_tcp; zl->_read_exact_f = _z_f_link_read_exact_tcp; zl->_read_socket_f = _z_f_link_tcp_read_socket; return ret; } #else z_result_t _z_endpoint_tcp_valid(_z_endpoint_t *endpoint) { _ZP_UNUSED(endpoint); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_new_peer_tcp(_z_endpoint_t *endpoint, _z_sys_net_socket_t *socket) { _ZP_UNUSED(endpoint); _ZP_UNUSED(socket); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_new_link_tcp(_z_link_t *zl, _z_endpoint_t *endpoint) { _ZP_UNUSED(zl); _ZP_UNUSED(endpoint); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif ================================================ FILE: src/link/unicast/tls.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/tls.h" #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/link.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/link/transport/tls_stream.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/string.h" #if Z_FEATURE_LINK_TLS == 1 uint16_t _z_get_link_mtu_tls(void) { return 65535; } z_result_t _z_endpoint_tls_valid(_z_endpoint_t *endpoint) { _z_string_t tls_str = _z_string_alias_str(TLS_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &tls_str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _z_tcp_address_valid(&endpoint->_locator._address); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); } return ret; } static _z_config_t _z_tls_merge_config(_z_str_intmap_t *endpoint_cfg, const _z_config_t *session_cfg) { _z_config_t cfg; if (endpoint_cfg != NULL) { _z_str_intmap_move(&cfg, endpoint_cfg); } else { cfg = _z_str_intmap_make(); } if (session_cfg == NULL) { return cfg; } static const struct { uint8_t locator_key; uint8_t session_key; } mapping[] = {{TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY, Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY}, {TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_KEY, Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY}, {TLS_CONFIG_LISTEN_PRIVATE_KEY_KEY, Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_KEY}, {TLS_CONFIG_LISTEN_PRIVATE_KEY_BASE64_KEY, Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY}, {TLS_CONFIG_LISTEN_CERTIFICATE_KEY, Z_CONFIG_TLS_LISTEN_CERTIFICATE_KEY}, {TLS_CONFIG_LISTEN_CERTIFICATE_BASE64_KEY, Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY}, {TLS_CONFIG_ENABLE_MTLS_KEY, Z_CONFIG_TLS_ENABLE_MTLS_KEY}, {TLS_CONFIG_CONNECT_PRIVATE_KEY_KEY, Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_KEY}, {TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_KEY, Z_CONFIG_TLS_CONNECT_PRIVATE_KEY_BASE64_KEY}, {TLS_CONFIG_CONNECT_CERTIFICATE_KEY, Z_CONFIG_TLS_CONNECT_CERTIFICATE_KEY}, {TLS_CONFIG_CONNECT_CERTIFICATE_BASE64_KEY, Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY}, {TLS_CONFIG_VERIFY_NAME_ON_CONNECT_KEY, Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY}}; for (size_t i = 0; i < sizeof(mapping) / sizeof(mapping[0]); i++) { if (_z_config_get(&cfg, mapping[i].locator_key) != NULL) { continue; } const char *value = _z_config_get(session_cfg, mapping[i].session_key); if (value != NULL) { _zp_config_insert(&cfg, mapping[i].locator_key, value); } } return cfg; } static z_result_t _z_f_link_open_tls(_z_link_t *self) { z_result_t ret = _Z_RES_OK; char *hostname = _z_tcp_address_parse_host(&self->_endpoint._locator._address); if (hostname == NULL) { _Z_ERROR("Failed to parse TLS endpoint address"); z_free(hostname); return _Z_ERR_GENERIC; } _z_sys_net_endpoint_t rep = {0}; ret = _z_tcp_endpoint_init_from_address(&rep, &self->_endpoint._locator._address); if (ret != _Z_RES_OK) { z_free(hostname); return ret; } ret = _z_open_tls(&self->_socket._tls, &rep, hostname, &self->_endpoint._config, false); _z_tcp_endpoint_clear(&rep); z_free(hostname); if (ret != _Z_RES_OK) { _Z_ERROR("TLS open failed"); } return ret; } static z_result_t _z_f_link_listen_tls(_z_link_t *self) { z_result_t ret = _Z_RES_OK; char *host = _z_tcp_address_parse_host(&self->_endpoint._locator._address); if (host == NULL) { _Z_ERROR("Invalid TLS endpoint"); z_free(host); return _Z_ERR_GENERIC; } _z_sys_net_endpoint_t rep = {0}; ret = _z_tcp_endpoint_init_from_address(&rep, &self->_endpoint._locator._address); if (ret != _Z_RES_OK) { z_free(host); return ret; } ret = _z_listen_tls(&self->_socket._tls, &rep, &self->_endpoint._config); _z_tcp_endpoint_clear(&rep); if (ret != _Z_RES_OK) { _Z_ERROR("TLS listen failed"); } z_free(host); return ret; } static void _z_f_link_close_tls(_z_link_t *self) { _z_close_tls(&self->_socket._tls); } static size_t _z_f_link_write_tls(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { // Use provided socket if available, otherwise fall back to link socket if (socket != NULL && socket->_tls_sock != NULL) { return _z_write_tls((_z_tls_socket_t *)socket->_tls_sock, ptr, len); } else { return _z_write_tls(&self->_socket._tls, ptr, len); } } static size_t _z_f_link_write_all_tls(const _z_link_t *self, const uint8_t *ptr, size_t len) { return _z_write_all_tls(&self->_socket._tls, ptr, len); } static size_t _z_f_link_read_tls(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(addr); return _z_read_tls(&self->_socket._tls, ptr, len); } static size_t _z_f_link_read_exact_tls(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(addr); size_t n = (size_t)0; do { size_t rb; if (socket != NULL && socket->_tls_sock != NULL) { rb = _z_read_tls((_z_tls_socket_t *)socket->_tls_sock, &ptr[n], len - n); } else { rb = _z_read_tls(&self->_socket._tls, &ptr[n], len - n); } if (rb == SIZE_MAX) { n = rb; break; } n += rb; } while (n != len); return n; } static size_t _z_f_link_tls_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { if (socket._tls_sock == NULL) { _Z_ERROR("TLS context not found in socket"); return SIZE_MAX; } return _z_read_tls((_z_tls_socket_t *)socket._tls_sock, ptr, len); } static void _z_f_link_free_tls(_z_link_t *self) { _ZP_UNUSED(self); } z_result_t _z_new_link_tls(_z_link_t *zl, _z_endpoint_t *endpoint, const _z_config_t *session_cfg) { zl->_type = _Z_LINK_TYPE_TLS; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_UNICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_STREAM; zl->_cap._is_reliable = true; zl->_mtu = _z_get_link_mtu_tls(); _z_config_t cfg = _z_tls_merge_config(&endpoint->_config, session_cfg); zl->_endpoint = *endpoint; zl->_endpoint._config = cfg; _z_str_intmap_clear(&endpoint->_config); _Z_DEBUG("TLS locator: '%.*s'", (int)_z_string_len(&endpoint->_locator._address), _z_string_data(&endpoint->_locator._address)); zl->_open_f = _z_f_link_open_tls; zl->_listen_f = _z_f_link_listen_tls; zl->_close_f = _z_f_link_close_tls; zl->_write_f = _z_f_link_write_tls; zl->_write_all_f = _z_f_link_write_all_tls; zl->_read_f = _z_f_link_read_tls; zl->_read_exact_f = _z_f_link_read_exact_tls; zl->_read_socket_f = _z_f_link_tls_read_socket; zl->_free_f = _z_f_link_free_tls; return _Z_RES_OK; } z_result_t _z_new_peer_tls(_z_endpoint_t *endpoint, _z_sys_net_socket_t *socket, const _z_config_t *session_cfg) { _z_sys_net_endpoint_t sys_endpoint = {0}; char *hostname = _z_tcp_address_parse_host(&endpoint->_locator._address); z_result_t ret = _Z_RES_OK; if (hostname == NULL) { ret = _Z_ERR_CONFIG_LOCATOR_INVALID; goto cleanup; } ret = _z_tcp_endpoint_init_from_address(&sys_endpoint, &endpoint->_locator._address); if (ret != _Z_RES_OK) { goto cleanup; } socket->_tls_sock = z_malloc(sizeof(_z_tls_socket_t)); if (socket->_tls_sock == NULL) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; goto cleanup; } _z_config_t cfg = _z_tls_merge_config(&endpoint->_config, session_cfg); ret = _z_open_tls((_z_tls_socket_t *)socket->_tls_sock, &sys_endpoint, hostname, &cfg, true); if (ret != _Z_RES_OK) { z_free(socket->_tls_sock); socket->_tls_sock = NULL; _z_config_clear(&cfg); _z_str_intmap_clear(&endpoint->_config); goto cleanup; } socket->_fd = ((_z_tls_socket_t *)socket->_tls_sock)->_sock._fd; _z_config_clear(&cfg); _z_str_intmap_clear(&endpoint->_config); cleanup: z_free(hostname); _z_tcp_endpoint_clear(&sys_endpoint); return ret; } #endif // Z_FEATURE_LINK_TLS == 1 ================================================ FILE: src/link/unicast/udp.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/udp.h" #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/udp_unicast.h" #if Z_FEATURE_LINK_UDP_UNICAST == 1 z_result_t _z_endpoint_udp_unicast_valid(_z_endpoint_t *endpoint) { _z_string_t udp_str = _z_string_alias_str(UDP_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &udp_str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _z_udp_unicast_address_valid(&endpoint->_locator._address); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); } return ret; } z_result_t _z_f_link_open_udp_unicast(_z_link_t *self) { uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } return _z_udp_unicast_open(&self->_socket._udp._sock, self->_socket._udp._rep, tout); } z_result_t _z_f_link_listen_udp_unicast(_z_link_t *self) { uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&self->_endpoint._config, UDP_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } return _z_udp_unicast_listen(&self->_socket._udp._sock, self->_socket._udp._rep, tout); } void _z_f_link_close_udp_unicast(_z_link_t *self) { _z_udp_unicast_close(&self->_socket._udp._sock); } void _z_f_link_free_udp_unicast(_z_link_t *self) { _z_udp_unicast_endpoint_clear(&self->_socket._udp._rep); } size_t _z_f_link_write_udp_unicast(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { if (socket != NULL) { return _z_udp_unicast_write(*socket, ptr, len, self->_socket._udp._rep); } else { return _z_udp_unicast_write(self->_socket._udp._sock, ptr, len, self->_socket._udp._rep); } } size_t _z_f_link_write_all_udp_unicast(const _z_link_t *self, const uint8_t *ptr, size_t len) { return _z_udp_unicast_write(self->_socket._udp._sock, ptr, len, self->_socket._udp._rep); } size_t _z_f_link_read_udp_unicast(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(addr); return _z_udp_unicast_read(self->_socket._udp._sock, ptr, len); } size_t _z_f_link_read_exact_udp_unicast(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(addr); if (socket != NULL) { return _z_udp_unicast_read_exact(*socket, ptr, len); } else { return _z_udp_unicast_read_exact(self->_socket._udp._sock, ptr, len); } } size_t _z_f_link_udp_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_udp_unicast_read(socket, ptr, len); } uint16_t _z_get_link_mtu_udp_unicast(void) { // @TODO: the return value should change depending on the target platform. return 1450; } z_result_t _z_new_link_udp_unicast(_z_link_t *zl, _z_endpoint_t endpoint) { zl->_type = _Z_LINK_TYPE_UDP; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_UNICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_DATAGRAM; zl->_cap._is_reliable = false; zl->_mtu = _z_get_link_mtu_udp_unicast(); zl->_endpoint = endpoint; z_result_t ret = _z_udp_unicast_endpoint_init_from_address(&zl->_socket._udp._rep, &endpoint._locator._address); zl->_open_f = _z_f_link_open_udp_unicast; zl->_listen_f = _z_f_link_listen_udp_unicast; zl->_close_f = _z_f_link_close_udp_unicast; zl->_free_f = _z_f_link_free_udp_unicast; zl->_write_f = _z_f_link_write_udp_unicast; zl->_write_all_f = _z_f_link_write_all_udp_unicast; zl->_read_f = _z_f_link_read_udp_unicast; zl->_read_exact_f = _z_f_link_read_exact_udp_unicast; zl->_read_socket_f = _z_f_link_udp_read_socket; return ret; } #endif ================================================ FILE: src/link/unicast/ws.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/config/ws.h" #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/tcp.h" #include "zenoh-pico/link/transport/ws.h" #if Z_FEATURE_LINK_WS == 1 static z_result_t _z_ws_address_valid(const _z_string_t *address) { return _z_tcp_address_valid(address); } z_result_t _z_endpoint_ws_valid(_z_endpoint_t *endpoint) { _z_string_t str = _z_string_alias_str(WS_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &str)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _z_ws_address_valid(&endpoint->_locator._address); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); } return ret; } z_result_t _z_f_link_open_ws(_z_link_t *zl) { uint32_t tout = Z_CONFIG_SOCKET_TIMEOUT; char *tout_as_str = _z_str_intmap_get(&zl->_endpoint._config, WS_CONFIG_TOUT_KEY); if (tout_as_str != NULL) { tout = (uint32_t)strtoul(tout_as_str, NULL, 10); } return _z_ws_transport_open(&zl->_socket._ws, tout); } z_result_t _z_f_link_listen_ws(_z_link_t *zl) { return _z_ws_transport_listen(&zl->_socket._ws); } void _z_f_link_close_ws(_z_link_t *zl) { _z_ws_transport_close(&zl->_socket._ws); } void _z_f_link_free_ws(_z_link_t *zl) { _z_ws_endpoint_clear(&zl->_socket._ws._rep); } size_t _z_f_link_write_ws(const _z_link_t *zl, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); return _z_ws_transport_write(&zl->_socket._ws, ptr, len); } size_t _z_f_link_write_all_ws(const _z_link_t *zl, const uint8_t *ptr, size_t len) { return _z_ws_transport_write(&zl->_socket._ws, ptr, len); } size_t _z_f_link_read_ws(const _z_link_t *zl, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(addr); return _z_ws_transport_read(&zl->_socket._ws, ptr, len); } size_t _z_f_link_read_exact_ws(const _z_link_t *zl, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(addr); _ZP_UNUSED(socket); return _z_ws_transport_read_exact(&zl->_socket._ws, ptr, len); } size_t _z_f_link_ws_read_socket(const _z_sys_net_socket_t socket, uint8_t *ptr, size_t len) { return _z_ws_transport_read_socket(socket, ptr, len); } uint16_t _z_get_link_mtu_ws(void) { // Maximum MTU for TCP return 65535; } z_result_t _z_new_link_ws(_z_link_t *zl, _z_endpoint_t *endpoint) { zl->_type = _Z_LINK_TYPE_WS; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_UNICAST; zl->_cap._flow = Z_LINK_CAP_FLOW_DATAGRAM; zl->_cap._is_reliable = true; zl->_mtu = _z_get_link_mtu_ws(); zl->_endpoint = *endpoint; z_result_t ret = _z_ws_endpoint_init(&zl->_socket._ws._rep, &endpoint->_locator._address); zl->_open_f = _z_f_link_open_ws; zl->_listen_f = _z_f_link_listen_ws; zl->_close_f = _z_f_link_close_ws; zl->_free_f = _z_f_link_free_ws; zl->_write_f = _z_f_link_write_ws; zl->_write_all_f = _z_f_link_write_all_ws; zl->_read_f = _z_f_link_read_ws; zl->_read_exact_f = _z_f_link_read_exact_ws; zl->_read_socket_f = _z_f_link_ws_read_socket; return ret; } #endif ================================================ FILE: src/net/config.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/config.h" #include #include "zenoh-pico/net/config.h" _z_config_t _z_config_empty(void) { _z_config_t config; _z_config_init(&config); return config; } z_result_t _z_config_default(_z_config_t *config) { return _z_config_client(config, NULL); } z_result_t _z_config_client(_z_config_t *ps, const char *locator) { *ps = _z_config_empty(); _Z_RETURN_IF_ERR(_zp_config_insert(ps, Z_CONFIG_MODE_KEY, Z_CONFIG_MODE_CLIENT)); if (locator != NULL) { // Connect only to the provided locator _Z_CLEAN_RETURN_IF_ERR(_zp_config_insert(ps, Z_CONFIG_CONNECT_KEY, locator), _z_config_clear(ps)); } else { // The locator is not provided, we should perform scouting _Z_CLEAN_RETURN_IF_ERR( _zp_config_insert(ps, Z_CONFIG_MULTICAST_SCOUTING_KEY, Z_CONFIG_MULTICAST_SCOUTING_DEFAULT), _z_config_clear(ps)); _Z_CLEAN_RETURN_IF_ERR( _zp_config_insert(ps, Z_CONFIG_MULTICAST_LOCATOR_KEY, Z_CONFIG_MULTICAST_LOCATOR_DEFAULT), _z_config_clear(ps)); _Z_CLEAN_RETURN_IF_ERR(_zp_config_insert(ps, Z_CONFIG_SCOUTING_TIMEOUT_KEY, Z_CONFIG_SCOUTING_TIMEOUT_DEFAULT), _z_config_clear(ps)); } return _Z_RES_OK; } ================================================ FILE: src/net/encoding.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/encoding.h" #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" _z_encoding_t _z_encoding_wrap(uint16_t id, const char *schema) { return (_z_encoding_t){.id = id, .schema = (schema == NULL) ? _z_string_null() : _z_string_alias_str((char *)schema)}; } z_result_t _z_encoding_make(_z_encoding_t *encoding, uint16_t id, const char *schema, size_t len) { encoding->id = id; // Clone schema if (schema != NULL) { encoding->schema = _z_string_copy_from_substr(schema, len); if (_z_string_len(&encoding->schema) != len) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } else { encoding->schema = _z_string_null(); } return _Z_RES_OK; } z_result_t _z_encoding_copy(_z_encoding_t *dst, const _z_encoding_t *src) { dst->id = src->id; if (_z_string_check(&src->schema)) { _Z_RETURN_IF_ERR(_z_string_copy(&dst->schema, &src->schema)); } else { dst->schema = _z_string_null(); } return _Z_RES_OK; } z_result_t _z_encoding_move(_z_encoding_t *dst, _z_encoding_t *src) { dst->id = src->id; src->id = _Z_ENCODING_ID_DEFAULT; dst->schema = _z_string_null(); if (_z_string_check(&src->schema)) { _Z_RETURN_IF_ERR(_z_string_move(&dst->schema, &src->schema)); } return _Z_RES_OK; } ================================================ FILE: src/net/filtering.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/net/filtering.h" #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/session/matching.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/locality.h" #if Z_FEATURE_INTEREST == 1 typedef struct _z_write_filter_registration_t { _z_write_filter_ctx_rc_t ctx_rc; struct _z_write_filter_registration_t *next; } _z_write_filter_registration_t; static bool _z_filter_target_peer_eq(const void *left, const void *right) { const _z_filter_target_t *left_val = (const _z_filter_target_t *)left; const _z_filter_target_t *right_val = (const _z_filter_target_t *)right; return left_val->peer == right_val->peer; } static bool _z_filter_target_eq(const void *left, const void *right) { const _z_filter_target_t *left_val = (const _z_filter_target_t *)left; const _z_filter_target_t *right_val = (const _z_filter_target_t *)right; return (left_val->peer == right_val->peer) && (left_val->decl_id == right_val->decl_id); } #if Z_FEATURE_MULTI_THREAD == 1 static void _z_write_filter_mutex_lock(_z_write_filter_ctx_t *ctx) { _z_mutex_lock(&ctx->mutex); } static void _z_write_filter_mutex_unlock(_z_write_filter_ctx_t *ctx) { _z_mutex_unlock(&ctx->mutex); } #else static void _z_write_filter_mutex_lock(_z_write_filter_ctx_t *ctx) { _ZP_UNUSED(ctx); } static void _z_write_filter_mutex_unlock(_z_write_filter_ctx_t *ctx) { _ZP_UNUSED(ctx); } #endif static bool _z_write_filter_push_target(_z_write_filter_ctx_t *ctx, _z_transport_peer_common_t *peer, uint32_t id) { _z_filter_target_t target = {.peer = (uintptr_t)peer, .decl_id = id}; ctx->targets = _z_filter_target_slist_push(ctx->targets, &target); if (ctx->targets == NULL) { // Allocation can fail return false; } return true; } static inline bool _z_write_filter_peer_allowed(const _z_write_filter_ctx_t *ctx, _z_transport_peer_common_t *peer) { return ((peer == NULL) && ctx->allow_local) || ((peer != NULL) && ctx->allow_remote); } static void _z_write_filter_ctx_update_state(_z_write_filter_ctx_t *ctx) { uint8_t prev_state = ctx->state; ctx->state = (ctx->targets == NULL && ctx->local_targets == 0) ? WRITE_FILTER_ACTIVE : WRITE_FILTER_OFF; if (prev_state != ctx->state) { _Z_DEBUG("Updated write filter state: %d", ctx->state); #if Z_FEATURE_MATCHING _z_closure_matching_status_intmap_iterator_t it = _z_closure_matching_status_intmap_iterator_make(&ctx->callbacks); _z_matching_status_t s = {.matching = ctx->state != WRITE_FILTER_ACTIVE}; while (_z_closure_matching_status_intmap_iterator_next(&it)) { _z_closure_matching_status_t *c = _z_closure_matching_status_intmap_iterator_value(&it); c->call(&s, c->context); } #endif } } #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 || Z_FEATURE_LOCAL_QUERYABLE == 1 static void _z_write_filter_ctx_add_local_match(_z_write_filter_ctx_t *ctx) { _z_write_filter_mutex_lock(ctx); ctx->local_targets++; _z_write_filter_ctx_update_state(ctx); _z_write_filter_mutex_unlock(ctx); } static void _z_write_filter_ctx_remove_local_match(_z_write_filter_ctx_t *ctx) { _z_write_filter_mutex_lock(ctx); if (ctx->local_targets > 0) { ctx->local_targets--; } _z_write_filter_ctx_update_state(ctx); _z_write_filter_mutex_unlock(ctx); } #endif static z_result_t _z_write_filter_session_register(_z_session_t *session, _z_write_filter_ctx_t *ctx, _z_write_filter_ctx_rc_t *ctx_rc) { _z_write_filter_registration_t *registration = (_z_write_filter_registration_t *)z_malloc(sizeof(_z_write_filter_registration_t)); if (registration == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } registration->ctx_rc = _z_write_filter_ctx_rc_clone(ctx_rc); if (_Z_RC_IS_NULL(®istration->ctx_rc)) { z_free(registration); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (_z_session_mutex_lock_if_open(session) != _Z_RES_OK) { _Z_WARN("Failed to lock session for write filter registration - session may be closing"); _z_write_filter_ctx_rc_drop(®istration->ctx_rc); z_free(registration); return _Z_ERR_SESSION_CLOSED; } registration->next = session->_write_filters; session->_write_filters = registration; #if (Z_FEATURE_LOCAL_SUBSCRIBER == 1) || (Z_FEATURE_LOCAL_QUERYABLE == 1) if (ctx->allow_local) { #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 if (ctx->target_type == _Z_WRITE_FILTER_SUBSCRIBER) { _z_subscription_rc_slist_t *node = session->_subscriptions; while (node != NULL) { _z_subscription_t *sub = _Z_RC_IN_VAL(_z_subscription_rc_slist_value(node)); if (_z_locality_allows_local(sub->_allowed_origin) && _z_keyexpr_intersects(&ctx->key, &sub->_key._inner)) { _z_write_filter_ctx_add_local_match(ctx); } node = _z_subscription_rc_slist_next(node); } } #endif #if Z_FEATURE_LOCAL_QUERYABLE == 1 if (ctx->target_type == _Z_WRITE_FILTER_QUERYABLE) { _z_session_queryable_rc_slist_t *node = session->_local_queryable; while (node != NULL) { _z_session_queryable_t *queryable = _Z_RC_IN_VAL(_z_session_queryable_rc_slist_value(node)); if (_z_locality_allows_local(queryable->_allowed_origin)) { if (ctx->is_complete ? (queryable->_complete && _z_keyexpr_includes(&queryable->_key._inner, &ctx->key)) : _z_keyexpr_intersects(&ctx->key, &queryable->_key._inner)) { _z_write_filter_ctx_add_local_match(ctx); } } node = _z_session_queryable_rc_slist_next(node); } } #endif } #endif _z_session_mutex_unlock(session); ctx->registration = registration; return _Z_RES_OK; } static void _z_write_filter_session_unregister(_z_write_filter_ctx_t *ctx) { _z_write_filter_registration_t *registration = ctx->registration; if (registration == NULL) { return; } ctx->registration = NULL; _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(®istration->ctx_rc)->zn); if (_Z_RC_IS_NULL(&session_rc)) { _z_write_filter_ctx_rc_drop(®istration->ctx_rc); z_free(registration); return; } _z_session_t *session = _Z_RC_IN_VAL(&session_rc); _z_session_mutex_lock(session); _z_write_filter_registration_t **iter = &session->_write_filters; while (*iter != NULL && *iter != registration) { iter = &((*iter)->next); } if (*iter != NULL) { *iter = registration->next; } _z_session_mutex_unlock(session); _z_session_rc_drop(&session_rc); _z_write_filter_ctx_rc_drop(®istration->ctx_rc); z_free(registration); } static void _z_write_filter_callback(const _z_interest_msg_t *msg, _z_transport_peer_common_t *peer, void *arg) { _z_write_filter_ctx_t *ctx = (_z_write_filter_ctx_t *)arg; // Process message _z_write_filter_mutex_lock(ctx); switch (msg->type) { case _Z_INTEREST_MSG_TYPE_DECL_SUBSCRIBER: case _Z_INTEREST_MSG_TYPE_DECL_QUERYABLE: { // the message might be a redeclare - so we need to remove the previous one first _z_filter_target_t target = {.decl_id = msg->id, .peer = (uintptr_t)peer}; ctx->targets = _z_filter_target_slist_drop_first_filter(ctx->targets, _z_filter_target_eq, &target); bool peer_allowed = _z_write_filter_peer_allowed(ctx, peer); if (peer_allowed && (!ctx->is_complete || (msg->is_complete && (ctx->is_aggregate || _z_keyexpr_includes(msg->key, &ctx->key))))) { _z_write_filter_push_target(ctx, peer, msg->id); } break; } case _Z_INTEREST_MSG_TYPE_UNDECL_SUBSCRIBER: case _Z_INTEREST_MSG_TYPE_UNDECL_QUERYABLE: { _z_filter_target_t target = {.decl_id = msg->id, .peer = (uintptr_t)peer}; ctx->targets = _z_filter_target_slist_drop_first_filter(ctx->targets, _z_filter_target_eq, &target); } break; case _Z_INTEREST_MSG_TYPE_CONNECTION_DROPPED: { _z_filter_target_t target = {.decl_id = 0, .peer = (uintptr_t)peer}; ctx->targets = _z_filter_target_slist_drop_all_filter(ctx->targets, _z_filter_target_peer_eq, &target); } break; default: break; } _z_write_filter_ctx_update_state(ctx); _z_write_filter_mutex_unlock(ctx); } z_result_t _z_write_filter_create(const _z_session_rc_t *zn, _z_write_filter_t *filter, const _z_declared_keyexpr_t *keyexpr, uint8_t interest_flag, bool complete, z_locality_t locality) { uint8_t flags = interest_flag | _Z_INTEREST_FLAG_RESTRICTED | _Z_INTEREST_FLAG_CURRENT; if (_Z_RC_IN_VAL(zn)->_mode == Z_WHATAMI_CLIENT) { // Add client specific flags flags |= _Z_INTEREST_FLAG_KEYEXPRS | _Z_INTEREST_FLAG_AGGREGATE | _Z_INTEREST_FLAG_FUTURE; } else if (_Z_RC_IN_VAL(zn)->_mode == Z_WHATAMI_PEER && _z_session_has_router_peer(_Z_RC_IN_VAL(zn))) { // Add additional flags when in peer mode and connected to a router flags |= _Z_INTEREST_FLAG_KEYEXPRS | _Z_INTEREST_FLAG_FUTURE; } filter->ctx = _z_write_filter_ctx_rc_null(); _z_keyexpr_t ke; _Z_RETURN_IF_ERR(_z_keyexpr_copy(&ke, &keyexpr->_inner)); _z_write_filter_ctx_t *ctx = (_z_write_filter_ctx_t *)z_malloc(sizeof(_z_write_filter_ctx_t)); if (ctx == NULL) { _z_keyexpr_clear(&ke); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } #if Z_FEATURE_MULTI_THREAD == 1 _Z_CLEAN_RETURN_IF_ERR(_z_mutex_init(&ctx->mutex), _z_keyexpr_clear(&ke); z_free(ctx)); #endif ctx->state = WRITE_FILTER_ACTIVE; ctx->targets = _z_filter_target_slist_new(); #if Z_FEATURE_MATCHING _z_closure_matching_status_intmap_init(&ctx->callbacks); #endif bool expects_queryable = _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_QUERYABLES); assert(expects_queryable != _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_SUBSCRIBERS) && "write filter must target exactly one entity type"); ctx->is_complete = complete; ctx->is_aggregate = (flags & _Z_INTEREST_FLAG_AGGREGATE) != 0; ctx->allow_local = _z_locality_allows_local(locality); ctx->allow_remote = _z_locality_allows_remote(locality); ctx->target_type = expects_queryable ? _Z_WRITE_FILTER_QUERYABLE : _Z_WRITE_FILTER_SUBSCRIBER; ctx->key = ke; ctx->zn = _z_session_rc_clone_as_weak(zn); if (_Z_RC_IS_NULL(&ctx->zn)) { _z_write_filter_ctx_clear(ctx); z_free(ctx); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } ctx->local_targets = 0; ctx->registration = NULL; filter->ctx = _z_write_filter_ctx_rc_new(ctx); if (_Z_RC_IS_NULL(&filter->ctx)) { _z_write_filter_ctx_clear(ctx); z_free(ctx); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_write_filter_ctx_rc_t filter_ctx_clone = _z_write_filter_ctx_rc_clone(&filter->ctx); _z_void_rc_t ctx_void = _z_write_filter_ctx_rc_to_void(&filter_ctx_clone); filter->_interest_id = _z_add_interest(_Z_RC_IN_VAL(zn), keyexpr, _z_write_filter_callback, flags, &ctx_void); if (filter->_interest_id == 0) { _z_write_filter_ctx_rc_drop(&filter->ctx); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _Z_CLEAN_RETURN_IF_ERR(_z_write_filter_session_register(_Z_RC_IN_VAL(zn), ctx, &filter->ctx), _z_remove_interest(_Z_RC_IN_VAL(zn), filter->_interest_id); _z_write_filter_ctx_rc_drop(&filter->ctx)); return _Z_RES_OK; } z_result_t _z_write_filter_ctx_clear(_z_write_filter_ctx_t *ctx) { z_result_t res = _Z_RES_OK; _z_write_filter_session_unregister(ctx); _z_write_filter_mutex_lock(ctx); _z_filter_target_slist_free(&ctx->targets); #if Z_FEATURE_MATCHING _z_closure_matching_status_intmap_clear(&ctx->callbacks); #endif _z_keyexpr_clear(&ctx->key); _z_session_weak_drop(&ctx->zn); _z_write_filter_mutex_unlock(ctx); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&ctx->mutex); #endif return res; } z_result_t _z_write_filter_clear(_z_write_filter_t *filter) { if (_Z_RC_IS_NULL(&filter->ctx)) { return _Z_RES_OK; } _z_write_filter_session_unregister(_Z_RC_IN_VAL(&filter->ctx)); _z_session_rc_t s = _z_session_weak_upgrade_if_open(&_Z_RC_IN_VAL(&filter->ctx)->zn); if (!_Z_RC_IS_NULL(&s)) { _z_remove_interest(_Z_RC_IN_VAL(&s), filter->_interest_id); _z_session_rc_drop(&s); } _z_write_filter_ctx_remove_callbacks(_Z_RC_IN_VAL(&filter->ctx)); _z_write_filter_ctx_rc_drop(&filter->ctx); return _Z_RES_OK; } #if Z_FEATURE_MATCHING == 1 void _z_write_filter_ctx_remove_callback(_z_write_filter_ctx_t *ctx, size_t id) { _z_write_filter_mutex_lock(ctx); _z_closure_matching_status_intmap_remove(&ctx->callbacks, id); _z_write_filter_mutex_unlock(ctx); } void _z_write_filter_ctx_remove_callbacks(_z_write_filter_ctx_t *ctx) { _z_write_filter_mutex_lock(ctx); _z_closure_matching_status_intmap_clear(&ctx->callbacks); _z_write_filter_mutex_unlock(ctx); } z_result_t _z_write_filter_ctx_add_callback(_z_write_filter_ctx_t *ctx, size_t id, _z_closure_matching_status_t *v) { _z_closure_matching_status_t *ptr = (_z_closure_matching_status_t *)z_malloc(sizeof(_z_closure_matching_status_t)); if (ptr == NULL) { _z_closure_matching_status_clear(v); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } *ptr = *v; *v = (_z_closure_matching_status_t){NULL, NULL, NULL}; _z_write_filter_mutex_lock(ctx); if (!_z_write_filter_ctx_active(ctx)) { _z_matching_status_t s = (_z_matching_status_t){.matching = true}; ptr->call(&s, ptr->context); } _z_closure_matching_status_intmap_insert(&ctx->callbacks, id, ptr); _z_write_filter_mutex_unlock(ctx); return _Z_RES_OK; } #endif #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 || Z_FEATURE_LOCAL_QUERYABLE == 1 static void _z_write_filter_match_free(void **value) { if (value == NULL || *value == NULL) { return; } _z_write_filter_ctx_rc_t *ctx = (_z_write_filter_ctx_rc_t *)(*value); _z_write_filter_ctx_rc_drop(ctx); z_free(ctx); *value = NULL; } static void _z_write_filter_notify_local_entity(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool is_complete, _z_write_filter_target_type_t source_type, bool add) { if (!_z_locality_allows_local(allowed_origin)) { return; } if (_z_session_mutex_lock_if_open(session) != _Z_RES_OK) { return; } _z_list_t *matches = NULL; for (_z_write_filter_registration_t *registration = session->_write_filters; registration != NULL; registration = registration->next) { _z_write_filter_ctx_t *registration_ctx = _Z_RC_IN_VAL(®istration->ctx_rc); if (!(registration_ctx->allow_local && (registration_ctx->is_complete ? (is_complete && _z_keyexpr_includes(key, ®istration_ctx->key)) : _z_keyexpr_intersects(®istration_ctx->key, key)))) { continue; } if (registration_ctx->target_type != source_type) { continue; } _z_write_filter_ctx_rc_t *ctx_clone = (_z_write_filter_ctx_rc_t *)z_malloc(sizeof(_z_write_filter_ctx_rc_t)); if (ctx_clone == NULL) { _z_list_free(&matches, _z_write_filter_match_free); _z_session_mutex_unlock(session); return; } *ctx_clone = _z_write_filter_ctx_rc_clone(®istration->ctx_rc); _z_list_t *new_head = _z_list_push(matches, ctx_clone); assert(new_head != matches && "Failed to allocate write-filter match node"); matches = new_head; } _z_session_mutex_unlock(session); _z_list_t *it = matches; while (it != NULL) { _z_write_filter_ctx_rc_t *ctx_clone = (_z_write_filter_ctx_rc_t *)_z_list_value(it); _z_write_filter_ctx_t *matched_ctx = _Z_RC_IN_VAL(ctx_clone); if (add) { _z_write_filter_ctx_add_local_match(matched_ctx); } else { _z_write_filter_ctx_remove_local_match(matched_ctx); } it = _z_list_next(it); } _z_list_free(&matches, _z_write_filter_match_free); } void _z_write_filter_notify_subscriber(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool add) { _z_write_filter_notify_local_entity(session, key, allowed_origin, true, _Z_WRITE_FILTER_SUBSCRIBER, add); } void _z_write_filter_notify_queryable(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool is_complete, bool add) { _z_write_filter_notify_local_entity(session, key, allowed_origin, is_complete, _Z_WRITE_FILTER_QUERYABLE, add); } #else void _z_write_filter_notify_subscriber(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool add) { _ZP_UNUSED(session); _ZP_UNUSED(key); _ZP_UNUSED(allowed_origin); _ZP_UNUSED(add); } void _z_write_filter_notify_queryable(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool is_complete, bool add) { _ZP_UNUSED(session); _ZP_UNUSED(key); _ZP_UNUSED(allowed_origin); _ZP_UNUSED(is_complete); _ZP_UNUSED(add); } #endif // Z_FEATURE_LOCAL_SUBSCRIBER == 1 || Z_FEATURE_LOCAL_QUERYABLE == 1 #else // Z_FEATURE_INTEREST == 0 z_result_t _z_write_filter_create(const _z_session_rc_t *zn, _z_write_filter_t *filter, const _z_declared_keyexpr_t *keyexpr, uint8_t interest_flag, bool complete, z_locality_t locality) { _ZP_UNUSED(zn); _ZP_UNUSED(keyexpr); _ZP_UNUSED(filter); _ZP_UNUSED(interest_flag); _ZP_UNUSED(complete); _ZP_UNUSED(locality); return _Z_RES_OK; } z_result_t _z_write_filter_clear(_z_write_filter_t *filter) { _ZP_UNUSED(filter); return _Z_RES_OK; } void _z_write_filter_notify_subscriber(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool add) { _ZP_UNUSED(session); _ZP_UNUSED(key); _ZP_UNUSED(allowed_origin); _ZP_UNUSED(add); } void _z_write_filter_notify_queryable(_z_session_t *session, const _z_keyexpr_t *key, z_locality_t allowed_origin, bool is_complete, bool add) { _ZP_UNUSED(session); _ZP_UNUSED(key); _ZP_UNUSED(allowed_origin); _ZP_UNUSED(is_complete); _ZP_UNUSED(add); } #endif ================================================ FILE: src/net/liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/net/liveliness.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_LIVELINESS == 1 /**************** Liveliness Token ****************/ z_result_t _z_liveliness_send_declare_token(_z_session_t *zn, uint32_t id, const _z_declared_keyexpr_t *keyexpr) { _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_declaration_t declaration = _z_make_decl_token(&wireexpr, id); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); z_result_t ret = _z_send_declare(zn, &n_msg); _z_n_msg_clear(&n_msg); return ret; } z_result_t _z_liveliness_send_undeclare_token(_z_session_t *zn, uint32_t id, const _z_declared_keyexpr_t *keyexpr) { _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_declaration_t declaration = _z_make_undecl_token(id, &wireexpr); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); z_result_t ret = _z_send_undeclare(zn, &n_msg); _z_n_msg_clear(&n_msg); return ret; } z_result_t _z_declare_liveliness_token(const _z_session_rc_t *zn, _z_liveliness_token_t *ret_token, const _z_declared_keyexpr_t *keyexpr) { *ret_token = _z_liveliness_token_null(); _Z_DEBUG("Declare liveliness token (%.*s)", (int)_z_string_len(&keyexpr->_inner._keyexpr), _z_string_data(&keyexpr->_inner._keyexpr)); uint32_t id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); _z_declared_keyexpr_t ke; _Z_RETURN_IF_ERR(_z_declared_keyexpr_declare(zn, &ke, keyexpr)); _Z_CLEAN_RETURN_IF_ERR(_z_liveliness_send_declare_token(_Z_RC_IN_VAL(zn), id, &ke), _z_declared_keyexpr_clear(&ke)); z_result_t ret = _Z_RES_OK; if (_z_session_mutex_lock_if_open(_Z_RC_IN_VAL(zn)) != _Z_RES_OK) { _z_declared_keyexpr_clear(&ke); return _Z_ERR_SESSION_CLOSED; } const _z_declared_keyexpr_t *pkeyexpr = _z_declared_keyexpr_intmap_get(&_Z_RC_IN_VAL(zn)->_local_tokens, id); if (pkeyexpr != NULL) { // Already received this token _Z_ERROR("Duplicate token id %i", (int)id); ret = _Z_ERR_ENTITY_DECLARATION_FAILED; } else { _z_declared_keyexpr_t *ke_on_heap = (_z_declared_keyexpr_t *)z_malloc(sizeof(_z_declared_keyexpr_t)); if (ke_on_heap == NULL || _z_declared_keyexpr_intmap_insert(&_Z_RC_IN_VAL(zn)->_local_tokens, id, ke_on_heap) == NULL) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; z_free(ke_on_heap); } else { *ke_on_heap = ke; } } _z_session_mutex_unlock(_Z_RC_IN_VAL(zn)); if (ret != _Z_RES_OK) { _z_liveliness_send_undeclare_token(_Z_RC_IN_VAL(zn), id, &ke); _z_declared_keyexpr_clear(&ke); return ret; } ret_token->_id = id; ret_token->_zn = _z_session_rc_clone_as_weak(zn); return _Z_RES_OK; } z_result_t _z_undeclare_liveliness_token(_z_liveliness_token_t *token) { if (token == NULL || _Z_RC_IS_NULL(&token->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&token->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { return _Z_ERR_SESSION_CLOSED; } _z_session_t *zn = _Z_RC_IN_VAL(&sess_rc); #else _z_session_t *zn = _z_session_weak_as_unsafe_ptr(&token->_zn); #endif z_result_t ret; _z_declared_keyexpr_t *ke = NULL; _Z_DEBUG("Unregister liveliness token (%i)", (int)token->_id); _z_session_mutex_lock(zn); ke = _z_declared_keyexpr_intmap_extract(&zn->_local_tokens, token->_id); _z_session_mutex_unlock(zn); if (ke != NULL) { ret = _z_liveliness_send_undeclare_token(zn, token->_id, ke); _z_declared_keyexpr_clear( ke); // ke needs to be undeclared outside of mutex, since it might trigger resources update z_free(ke); } else { ret = _Z_ERR_ENTITY_UNKNOWN; } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } /**************** Liveliness Subscriber ****************/ z_result_t _z_liveliness_subscription_trigger_history(_z_session_t *zn, const _z_subscription_t *sub) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Retrieve liveliness history for %.*s", (int)_z_string_len(&sub->_key._inner._keyexpr), _z_string_data(&sub->_key._inner._keyexpr)); _z_keyexpr_slist_t *tokens_list = _z_keyexpr_slist_new(); _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); // TODO: could we call callbacks inside the mutex? - this would allow to avoid extra keyexpr copies, list // allocations, in addition it would also allow to stay consistent with respect to eventual remote undeclarations, // i.e. if they arrive during callback execution - they will only be delivered after initial history calls, // thus preventing potential declare/undeclare order inversion. // TODO: add support for local tokens _z_keyexpr_intmap_iterator_t iter = _z_keyexpr_intmap_iterator_make(&zn->_remote_tokens); while (_z_keyexpr_intmap_iterator_next(&iter)) { if (_z_keyexpr_intersects(&sub->_key._inner, _z_keyexpr_intmap_iterator_value(&iter))) { tokens_list = _z_keyexpr_slist_push(tokens_list, _z_keyexpr_intmap_iterator_value(&iter)); } } _z_session_mutex_unlock(zn); _z_keyexpr_slist_t *pos = tokens_list; while (pos != NULL) { _z_sample_t s = _z_sample_null(); s.kind = Z_SAMPLE_KIND_PUT; s.keyexpr._inner = _z_keyexpr_alias(_z_keyexpr_slist_value(pos)); sub->_callback(&s, sub->_arg); _z_sample_clear(&s); pos = _z_keyexpr_slist_next(pos); } _z_keyexpr_slist_free(&tokens_list); return ret; } #if Z_FEATURE_SUBSCRIPTION == 1 z_result_t _z_register_liveliness_subscriber(uint32_t *out_sub_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, bool history, void *arg, const _z_sync_group_t *callback_sync_group) { _z_subscription_t s = {0}; s._id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); s._callback = callback; s._dropper = dropper; s._arg = arg; s._allowed_origin = z_locality_default(); _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_declare(zn, &s._key, keyexpr), _z_subscription_clear(&s)); _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(&_Z_RC_IN_VAL(zn)->_callback_drop_sync_group, &s._session_callback_drop_notifier), _z_subscription_clear(&s)); if (callback_sync_group != NULL) { _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(callback_sync_group, &s._subscriber_callback_drop_notifier), _z_subscription_clear(&s)); } // Register subscription, stored at session-level, do not drop it by the end of this function. _z_subscription_rc_t sp_s = _z_register_subscription(_Z_RC_IN_VAL(zn), _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &s); if (_Z_RC_IS_NULL(&sp_s)) { _z_subscription_clear(&s); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (history) { _Z_CLEAN_RETURN_IF_ERR( _z_liveliness_subscription_trigger_history(_Z_RC_IN_VAL(zn), _Z_RC_IN_VAL(&sp_s)), _z_unregister_subscription(_Z_RC_IN_VAL(zn), _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &sp_s)); } // Build the declare message to send on the wire uint8_t mode = history ? (_Z_INTEREST_FLAG_CURRENT | _Z_INTEREST_FLAG_FUTURE) : _Z_INTEREST_FLAG_FUTURE; _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(&sp_s)->_key, _Z_RC_IN_VAL(zn)); _z_interest_t interest = _z_make_interest( &wireexpr, s._id, _Z_INTEREST_FLAG_KEYEXPRS | _Z_INTEREST_FLAG_TOKENS | _Z_INTEREST_FLAG_RESTRICTED | mode); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); z_result_t res = _z_send_declare(_Z_RC_IN_VAL(zn), &n_msg); _z_n_msg_clear(&n_msg); if (res != _Z_RES_OK) { _z_unregister_subscription(_Z_RC_IN_VAL(zn), _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &sp_s); res = _Z_ERR_TRANSPORT_TX_FAILED; } else { *out_sub_id = _Z_RC_IN_VAL(&sp_s)->_id; _z_subscription_rc_drop(&sp_s); } return res; } z_result_t _z_declare_liveliness_subscriber(_z_subscriber_t *subscriber, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, bool history, void *arg) { *subscriber = _z_subscriber_null(); subscriber->_zn = _z_session_rc_clone_as_weak(zn); z_result_t ret = _z_sync_group_create(&subscriber->_callback_drop_sync_group); _Z_SET_IF_OK(ret, _z_register_liveliness_subscriber(&subscriber->_entity_id, zn, keyexpr, callback, dropper, history, arg, &subscriber->_callback_drop_sync_group)); _Z_CLEAN_RETURN_IF_ERR(ret, _z_subscriber_clear(subscriber)); return _Z_RES_OK; } z_result_t _z_undeclare_liveliness_subscriber(_z_subscriber_t *sub) { if (sub == NULL || _Z_RC_IS_NULL(&sub->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } _z_subscription_rc_t s = _z_get_subscription_by_id(_Z_RC_IN_VAL(&sub->_zn), _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, sub->_entity_id); if (_Z_RC_IS_NULL(&s)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } _z_interest_t interest = _z_make_interest_final(_Z_RC_IN_VAL(&s)->_id); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); if (_z_send_undeclare(_Z_RC_IN_VAL(&sub->_zn), &n_msg) != _Z_RES_OK) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } _z_n_msg_clear(&n_msg); _z_unregister_subscription(_Z_RC_IN_VAL(&sub->_zn), _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &s); return _z_sync_group_check(&sub->_callback_drop_sync_group) ? _z_sync_group_wait(&sub->_callback_drop_sync_group) : _Z_RES_OK; } #endif // Z_FEATURE_SUBSCRIPTION == 1 /**************** Liveliness Query ****************/ #if Z_FEATURE_QUERY == 1 #ifdef Z_FEATURE_UNSTABLE_API typedef struct _z_cancel_liveliness_pending_query_arg_t { _z_session_weak_t _zn; uint32_t _qid; } _z_cancel_liveliness_pending_query_arg_t; z_result_t _z_cancel_liveliness_pending_query(void *arg) { _z_cancel_liveliness_pending_query_arg_t *a = (_z_cancel_liveliness_pending_query_arg_t *)arg; _z_session_rc_t s_rc = _z_session_weak_upgrade_if_open(&a->_zn); if (!_Z_RC_IS_NULL(&s_rc)) { _z_liveliness_unregister_pending_query(_Z_RC_IN_VAL(&s_rc), a->_qid); _z_session_rc_drop(&s_rc); } return _Z_RES_OK; } void _z_cancel_liveliness_pending_query_arg_drop(void *arg) { _z_cancel_liveliness_pending_query_arg_t *a = (_z_cancel_liveliness_pending_query_arg_t *)arg; _z_session_weak_drop(&a->_zn); z_free(a); } z_result_t _z_liveliness_pending_query_register_cancellation(_z_liveliness_pending_query_t *pq, const _z_cancellation_token_rc_t *opt_cancellation_token, const _z_session_rc_t *zn) { pq->_cancellation_data = _z_pending_query_cancellation_data_null(); if (opt_cancellation_token != NULL) { _z_cancel_liveliness_pending_query_arg_t *arg = (_z_cancel_liveliness_pending_query_arg_t *)z_malloc(sizeof(_z_cancel_liveliness_pending_query_arg_t)); if (arg == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } arg->_zn = _z_session_rc_clone_as_weak(zn); arg->_qid = pq->_id; size_t handler_id = 0; _z_cancellation_token_on_cancel_handler_t handler; handler._on_cancel = _z_cancel_liveliness_pending_query; handler._on_drop = _z_cancel_liveliness_pending_query_arg_drop; handler._arg = (void *)arg; _Z_CLEAN_RETURN_IF_ERR( _z_cancellation_token_add_on_cancel_handler(_Z_RC_IN_VAL(opt_cancellation_token), &handler, &handler_id), _z_cancellation_token_on_cancel_handler_drop(&handler)); pq->_cancellation_data._cancellation_token = _z_cancellation_token_rc_clone(opt_cancellation_token); pq->_cancellation_data._handler_id = handler_id; _Z_CLEAN_RETURN_IF_ERR( _z_cancellation_token_get_notifier(_Z_RC_IN_VAL(opt_cancellation_token), &pq->_cancellation_data._notifier), _z_pending_query_cancellation_data_clear(&pq->_cancellation_data)); } return _Z_RES_OK; } #endif z_result_t _z_liveliness_query(const _z_session_rc_t *session, const _z_declared_keyexpr_t *keyexpr, _z_closure_reply_callback_t callback, _z_drop_handler_t dropper, void *arg, uint64_t timeout_ms, _z_cancellation_token_rc_t *opt_cancellation_token) { z_result_t ret = _Z_RES_OK; _z_session_t *zn = _Z_RC_IN_VAL(session); _Z_DEBUG("Register liveliness query for (%.*s)", (int)_z_string_len(&keyexpr->_inner._keyexpr), _z_string_data(&keyexpr->_inner._keyexpr)); _z_keyexpr_t query_ke; _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_copy(&query_ke, &keyexpr->_inner), _z_drop_handler_execute(dropper, arg)); uint32_t query_id; _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&query_ke); _z_drop_handler_execute(dropper, arg);); _z_liveliness_pending_query_t *pq = _z_unsafe_liveliness_register_pending_query(zn); if (pq == NULL) { _z_session_mutex_unlock(zn); _z_keyexpr_clear(&query_ke); _z_drop_handler_execute(dropper, arg); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } query_id = pq->_id; pq->_key = query_ke; pq->_callback = callback; pq->_dropper = dropper; pq->_arg = arg; #ifdef Z_FEATURE_UNSTABLE_API ret = _z_liveliness_pending_query_register_cancellation(pq, opt_cancellation_token, session); #else _ZP_UNUSED(opt_cancellation_token); #endif _z_session_mutex_unlock(zn); if (ret == _Z_RES_OK) { _ZP_UNUSED(timeout_ms); // Current interest in pico don't support timeout _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_interest_t interest = _z_make_interest(&wireexpr, query_id, _Z_INTEREST_FLAG_KEYEXPRS | _Z_INTEREST_FLAG_TOKENS | _Z_INTEREST_FLAG_RESTRICTED | _Z_INTEREST_FLAG_CURRENT); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); ret = _z_send_declare(zn, &n_msg); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; } _z_n_msg_clear(&n_msg); } _Z_CLEAN_RETURN_IF_ERR(ret, _z_liveliness_unregister_pending_query(zn, query_id)); return ret; } #endif // Z_FEATURE_QUERY == 1 #endif // Z_FEATURE_LIVELINESS == 1 ================================================ FILE: src/net/logger.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/net/logger.h" /*------------------ Init/Config ------------------*/ void _z_init_logger(void) { // @TODO } ================================================ FILE: src/net/matching.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/net/matching.h" #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/session/matching.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_MATCHING == 1 z_result_t _z_matching_listener_register(uint32_t *listener_id, _z_session_t *s, const _z_write_filter_ctx_rc_t *ctx, _z_closure_matching_status_t *callback) { *listener_id = _z_get_entity_id(s); _z_closure_matching_status_t c; c = *callback; *callback = (_z_closure_matching_status_t){NULL, NULL, NULL}; return _z_write_filter_ctx_add_callback(_Z_RC_IN_VAL(ctx), *listener_id, &c); } z_result_t _z_matching_listener_declare(_z_matching_listener_t *listener, _z_session_t *s, const _z_write_filter_ctx_rc_t *ctx, _z_closure_matching_status_t *callback) { *listener = _z_matching_listener_null(); listener->_write_filter_ctx = _z_write_filter_ctx_rc_clone_as_weak(ctx); _Z_CLEAN_RETURN_IF_ERR(_z_matching_listener_register(&listener->_id, s, ctx, callback), _z_matching_listener_clear(listener)); return _Z_RES_OK; } z_result_t _z_matching_listener_undeclare(_z_matching_listener_t *listener) { _z_write_filter_ctx_rc_t write_filter = _z_write_filter_ctx_weak_upgrade(&listener->_write_filter_ctx); if (!_Z_RC_IS_NULL(&write_filter)) { _z_write_filter_ctx_remove_callback(_Z_RC_IN_VAL(&write_filter), listener->_id); _z_write_filter_ctx_rc_drop(&write_filter); } return _Z_RES_OK; } void _z_matching_listener_clear(_z_matching_listener_t *listener) { _z_write_filter_ctx_weak_drop(&listener->_write_filter_ctx); *listener = _z_matching_listener_null(); } void _z_matching_listener_free(_z_matching_listener_t **listener) { _z_matching_listener_t *ptr = *listener; if (ptr != NULL) { _z_matching_listener_clear(ptr); z_free(ptr); *listener = NULL; } } #endif ================================================ FILE: src/net/memory.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include "zenoh-pico/net/sample.h" #include "zenoh-pico/protocol/core.h" void _z_hello_clear(_z_hello_t *hello) { _z_string_svec_clear(&hello->_locators); } void _z_hello_free(_z_hello_t **hello) { _z_hello_t *ptr = *hello; if (ptr != NULL) { _z_hello_clear(ptr); z_free(ptr); *hello = NULL; } } void _z_value_clear(_z_value_t *value) { _z_encoding_clear(&value->encoding); _z_bytes_drop(&value->payload); } void _z_value_free(_z_value_t **value) { _z_value_t *ptr = *value; if (ptr != NULL) { _z_value_clear(ptr); z_free(ptr); *value = NULL; } } bool _z_hello_check(const _z_hello_t *hello) { return _z_id_check(hello->_zid); } ================================================ FILE: src/net/primitives.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/primitives.h" #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/liveliness.h" #include "zenoh-pico/net/logger.h" #include "zenoh-pico/net/matching.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/loopback.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/result.h" #include "zenoh-pico/utils/string.h" /*------------------ Declaration Helpers ------------------*/ z_result_t _z_send_declare(_z_session_t *zn, const _z_network_message_t *n_msg) { z_result_t ret = _Z_RES_OK; ret = _z_send_n_msg(zn, n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); #if Z_FEATURE_AUTO_RECONNECT == 1 if (ret == _Z_RES_OK) { _z_cache_declaration(zn, n_msg); } #endif return ret; } z_result_t _z_send_undeclare(_z_session_t *zn, const _z_network_message_t *n_msg) { z_result_t ret = _Z_RES_OK; ret = _z_send_n_msg(zn, n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); #if Z_FEATURE_AUTO_RECONNECT == 1 if (ret == _Z_RES_OK) { _z_prune_declaration(zn, n_msg); } #endif return ret; } /*------------------ Scouting ------------------*/ #if Z_FEATURE_SCOUTING == 1 void _z_scout(const z_what_t what, const _z_id_t zid, _z_string_t *locator, const uint32_t timeout, _z_closure_hello_callback_t callback, void *arg_call, _z_drop_handler_t dropper, void *arg_drop) { _z_hello_slist_t *hellos = _z_scout_inner(what, zid, locator, timeout, false); while (hellos != NULL) { _z_hello_t *hello = _z_hello_slist_value(hellos); (*callback)(hello, arg_call); hellos = _z_hello_slist_pop(hellos); } if (dropper != NULL) { (*dropper)(arg_drop); } _z_hello_slist_free(&hellos); } #endif /*------------------ Resource Declaration ------------------*/ z_result_t _z_declare_resource(_z_session_t *zn, const _z_string_t *key, uint16_t *out_id) { _z_wireexpr_t expr = _z_wireexpr_null(); expr._id = Z_RESOURCE_ID_NONE; expr._suffix = _z_string_alias(*key); z_result_t ret = _z_register_resource(zn, &expr, Z_RESOURCE_ID_NONE, NULL, out_id); if (ret == _Z_RES_OK) { // Build the declare message to send on the wire _z_declaration_t declaration = _z_make_decl_keyexpr(*out_id, &expr); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); ret = _z_send_declare(zn, &n_msg); if (ret != _Z_RES_OK) { _z_unregister_resource(zn, *out_id, NULL); } _z_n_msg_clear(&n_msg); } return ret; } z_result_t _z_undeclare_resource(_z_session_t *zn, uint16_t rid) { _Z_DEBUG("Undeclaring local keyexpr %d", rid); z_result_t ret = _z_unregister_resource(zn, rid, NULL); if (ret == _Z_RES_OK) { // Build the declare message to send on the wire _z_declaration_t declaration = _z_make_undecl_keyexpr(rid); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); if (_z_send_undeclare(zn, &n_msg) != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; } _z_n_msg_clear(&n_msg); } else if (ret > 0) { ret = _Z_RES_OK; } else { _Z_ERROR_LOG(ret); } return ret; } #if Z_FEATURE_PUBLICATION == 1 /*------------------ Publisher Declaration ------------------*/ z_result_t _z_declare_publisher(_z_publisher_t *publisher, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_encoding_t *encoding, z_congestion_control_t congestion_control, z_priority_t priority, bool is_express, z_reliability_t reliability, z_locality_t allowed_destination) { publisher->_id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); publisher->_congestion_control = congestion_control; publisher->_priority = priority; publisher->_is_express = is_express; publisher->reliability = reliability; publisher->_zn = _z_session_rc_clone_as_weak(zn); publisher->_encoding = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); publisher->_allowed_destination = allowed_destination; publisher->_filter = (_z_write_filter_t){0}; _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_declare(zn, &publisher->_key, keyexpr), _z_undeclare_publisher(publisher)); return _Z_RES_OK; } z_result_t _z_undeclare_publisher(_z_publisher_t *pub) { if (pub == NULL || _Z_RC_IS_NULL(&pub->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } _z_write_filter_clear(&pub->_filter); _z_declared_keyexpr_clear(&pub->_key); _z_session_weak_drop(&pub->_zn); _z_encoding_clear(&pub->_encoding); *pub = _z_publisher_null(); return _Z_RES_OK; } /*------------------ Write ------------------*/ z_result_t _z_write(_z_session_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, z_congestion_control_t cong_ctrl, z_priority_t priority, bool is_express, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info, z_locality_t allowed_destination) { z_result_t ret = _Z_RES_OK; _z_qos_t qos = _z_n_qos_make(is_express, cong_ctrl == Z_CONGESTION_CONTROL_BLOCK, priority); if (_z_locality_allows_remote(allowed_destination)) { _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_network_message_t msg; switch (kind) { case Z_SAMPLE_KIND_PUT: _z_n_msg_make_push_put(&msg, &wireexpr, payload, encoding, qos, timestamp, attachment, reliability, source_info); break; case Z_SAMPLE_KIND_DELETE: _z_n_msg_make_push_del(&msg, &wireexpr, qos, timestamp, reliability, source_info); break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (_z_send_n_msg(zn, &msg, reliability, cong_ctrl, NULL) != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; } } #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 if (ret == _Z_RES_OK && _z_locality_allows_local(allowed_destination)) { ret = _z_session_deliver_push_locally(zn, &keyexpr->_inner, payload, encoding, kind, qos, timestamp, attachment, reliability, source_info); } #endif return ret; } #endif #if Z_FEATURE_SUBSCRIPTION == 1 /*------------------ Subscriber Declaration ------------------*/ z_result_t _z_register_subscriber(uint32_t *sub_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin, const _z_sync_group_t *callback_drop_sync_group) { _z_subscription_t s = {0}; s._id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); s._callback = callback; s._dropper = dropper; s._arg = arg; s._allowed_origin = allowed_origin; _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_declare_non_wild_prefix(zn, &s._key, keyexpr), _z_subscription_clear(&s)); _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(&_Z_RC_IN_VAL(zn)->_callback_drop_sync_group, &s._session_callback_drop_notifier), _z_subscription_clear(&s)); if (callback_drop_sync_group != NULL) { _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(callback_drop_sync_group, &s._subscriber_callback_drop_notifier), _z_subscription_clear(&s)); } _z_subscription_rc_t sp_s = _z_register_subscription(_Z_RC_IN_VAL(zn), _Z_SUBSCRIBER_KIND_SUBSCRIBER, &s); if (_Z_RC_IS_NULL(&sp_s)) { _z_subscription_clear(&s); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (_z_locality_allows_remote(allowed_origin)) { _z_wireexpr_t wire_expr = _z_declared_keyexpr_alias_to_wire(keyexpr, _Z_RC_IN_VAL(zn)); _z_declaration_t declaration = _z_make_decl_subscriber(&wire_expr, s._id); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); z_result_t res = _z_send_declare(_Z_RC_IN_VAL(zn), &n_msg); _z_n_msg_clear(&n_msg); if (res != _Z_RES_OK) { _z_unregister_subscription(_Z_RC_IN_VAL(zn), _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sp_s); return res; } } *sub_id = s._id; _z_subscription_rc_drop(&sp_s); // we do not keep this data for the time being inside subscriber, and rc copy is // still stored inside the session return _Z_RES_OK; } z_result_t _z_declare_subscriber(_z_subscriber_t *subscriber, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_closure_sample_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin) { *subscriber = _z_subscriber_null(); subscriber->_zn = _z_session_rc_clone_as_weak(zn); z_result_t ret = _z_sync_group_create(&subscriber->_callback_drop_sync_group); _Z_SET_IF_OK(ret, _z_register_subscriber(&subscriber->_entity_id, zn, keyexpr, callback, dropper, arg, allowed_origin, &subscriber->_callback_drop_sync_group)); _Z_CLEAN_RETURN_IF_ERR(ret, _z_subscriber_clear(subscriber)); return _Z_RES_OK; } z_result_t _z_undeclare_subscriber(_z_subscriber_t *sub) { if (sub == NULL || _Z_RC_IS_NULL(&sub->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&sub->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { return _Z_ERR_SESSION_CLOSED; } _z_session_t *zn = _Z_RC_IN_VAL(&sess_rc); #else _z_session_t *zn = _z_session_weak_as_unsafe_ptr(&sub->_zn); #endif // Find subscription entry _z_subscription_rc_t s = _z_get_subscription_by_id(zn, _Z_SUBSCRIBER_KIND_SUBSCRIBER, sub->_entity_id); if (_Z_RC_IS_NULL(&s)) { #if Z_FEATURE_LIVELINESS == 1 #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif // Not found, treat it as a liveliness subscriber return _z_undeclare_liveliness_subscriber(sub); #else _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); #endif } z_result_t ret = _Z_RES_OK; if (_z_locality_allows_remote(_Z_RC_IN_VAL(&s)->_allowed_origin)) { _z_declaration_t declaration; if (zn->_mode == Z_WHATAMI_CLIENT) { declaration = _z_make_undecl_subscriber(sub->_entity_id, NULL); } else { _z_wireexpr_t expr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(&s)->_key, zn); declaration = _z_make_undecl_subscriber(sub->_entity_id, &expr); } _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); if (_z_send_undeclare(zn, &n_msg) != _Z_RES_OK) { ret = _Z_ERR_TRANSPORT_TX_FAILED; } _z_n_msg_clear(&n_msg); } _z_unregister_subscription(zn, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &s); #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif z_result_t wait_ret = _z_sync_group_check(&sub->_callback_drop_sync_group) ? _z_sync_group_wait(&sub->_callback_drop_sync_group) : _Z_RES_OK; return ret == _Z_RES_OK ? wait_ret : ret; } #endif #if Z_FEATURE_QUERYABLE == 1 /*------------------ Queryable Declaration ------------------*/ z_result_t _z_register_queryable(uint32_t *queryable_id, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, bool complete, _z_closure_query_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin, const _z_sync_group_t *callback_drop_sync_group) { _z_session_queryable_t q = {0}; q._id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); q._complete = complete; q._callback = callback; q._dropper = dropper; q._arg = arg; q._allowed_origin = allowed_origin; _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_declare_non_wild_prefix(zn, &q._key, keyexpr), _z_session_queryable_clear(&q)); _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(&_Z_RC_IN_VAL(zn)->_callback_drop_sync_group, &q._session_callback_drop_notifier), _z_session_queryable_clear(&q)); if (callback_drop_sync_group != NULL) { _Z_CLEAN_RETURN_IF_ERR( _z_sync_group_create_notifier(callback_drop_sync_group, &q._queryable_callback_drop_notifier), _z_session_queryable_clear(&q)); } // Create session_queryable entry, stored at session-level, do not drop it by the end of this function. _z_session_queryable_rc_t sp_q = _z_register_session_queryable(_Z_RC_IN_VAL(zn), &q); if (_Z_RC_IS_NULL(&sp_q)) { _z_session_queryable_clear(&q); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (_z_locality_allows_remote(allowed_origin)) { // Build the declare message to send on the wire _z_wireexpr_t wire_expr = _z_declared_keyexpr_alias_to_wire(&q._key, _Z_RC_IN_VAL(zn)); _z_declaration_t declaration = _z_make_decl_queryable(&wire_expr, q._id, q._complete, _Z_QUERYABLE_DISTANCE_DEFAULT); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); z_result_t res = _z_send_declare(_Z_RC_IN_VAL(zn), &n_msg); _z_n_msg_clear(&n_msg); if (res != _Z_RES_OK) { _z_unregister_session_queryable(_Z_RC_IN_VAL(zn), &sp_q); return res; } } *queryable_id = q._id; _z_session_queryable_rc_drop(&sp_q); // we do not keep this data for the time being inside queryable, and rc copy // is still stored inside the session return _Z_RES_OK; } z_result_t _z_declare_queryable(_z_queryable_t *queryable, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, bool complete, _z_closure_query_callback_t callback, _z_drop_handler_t dropper, void *arg, z_locality_t allowed_origin) { *queryable = _z_queryable_null(); queryable->_zn = _z_session_rc_clone_as_weak(zn); z_result_t ret = _z_sync_group_create(&queryable->_callback_drop_sync_group); _Z_SET_IF_OK(ret, _z_register_queryable(&queryable->_entity_id, zn, keyexpr, complete, callback, dropper, arg, allowed_origin, &queryable->_callback_drop_sync_group)); _Z_CLEAN_RETURN_IF_ERR(ret, _z_queryable_clear(queryable)); return _Z_RES_OK; } z_result_t _z_undeclare_queryable(_z_queryable_t *qle) { if (qle == NULL || _Z_RC_IS_NULL(&qle->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&qle->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { return _Z_ERR_SESSION_CLOSED; } _z_session_t *zn = _Z_RC_IN_VAL(&sess_rc); #else _z_session_t *zn = _z_session_weak_as_unsafe_ptr(&sub->_zn); #endif // Find session_queryable entry _z_session_queryable_rc_t q = _z_get_session_queryable_by_id(zn, qle->_entity_id); z_result_t ret = _Z_RES_OK; if (_Z_RC_IS_NULL(&q)) { #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } if (_z_locality_allows_remote(_Z_RC_IN_VAL(&q)->_allowed_origin)) { // Build the declare message to send on the wire _z_declaration_t declaration; if (zn->_mode == Z_WHATAMI_CLIENT) { declaration = _z_make_undecl_queryable(qle->_entity_id, NULL); } else { _z_wireexpr_t expr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(&q)->_key, zn); declaration = _z_make_undecl_queryable(qle->_entity_id, &expr); } _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_none()); ret = _z_send_undeclare(zn, &n_msg); if (ret != _Z_RES_OK) { ret = _Z_ERR_TRANSPORT_TX_FAILED; } _z_n_msg_clear(&n_msg); } _z_unregister_session_queryable(zn, &q); z_result_t wait_ret = _z_sync_group_check(&qle->_callback_drop_sync_group) ? _z_sync_group_wait(&qle->_callback_drop_sync_group) : _Z_RES_OK; ret = ret == _Z_RES_OK ? wait_ret : ret; #if Z_FEATURE_SESSION_CHECK == 1 _z_session_rc_drop(&sess_rc); #endif return ret; } z_result_t _z_send_reply(const _z_query_t *query, const _z_session_rc_t *zsrc, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const z_sample_kind_t kind, bool is_express, const _z_timestamp_t *timestamp, _z_bytes_t *att, _z_source_info_t *source_info) { _z_session_t *zn = _Z_RC_IN_VAL(zsrc); _Z_DEBUG("send_reply: rid=%jd kind=%d", (intmax_t)query->_request_id, (int)kind); // Check key expression if (!query->_anyke && !_z_declared_keyexpr_intersects(&query->_key, keyexpr)) { _Z_ERROR_RETURN(_Z_ERR_KEYEXPR_NOT_MATCH); } // Build the reply context decorator. This is NOT the final reply. _z_n_qos_t qos = _z_n_qos_create(is_express, _z_n_qos_get_congestion_control(query->_qos), _z_n_qos_get_priority(query->_qos)); if (query->_is_local) { return _z_session_deliver_reply_locally(query, zsrc, keyexpr, payload, encoding, kind, qos, timestamp, att, source_info); } _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, _Z_RC_IN_VAL(zsrc)); _z_zenoh_message_t z_msg; switch (kind) { case Z_SAMPLE_KIND_PUT: _z_n_msg_make_reply_ok_put(&z_msg, &zn->_local_zid, query->_request_id, &wireexpr, Z_RELIABILITY_DEFAULT, Z_CONSOLIDATION_MODE_DEFAULT, qos, timestamp, source_info, payload, encoding, att); break; case Z_SAMPLE_KIND_DELETE: _z_n_msg_make_reply_ok_del(&z_msg, &zn->_local_zid, query->_request_id, &wireexpr, Z_RELIABILITY_DEFAULT, Z_CONSOLIDATION_MODE_DEFAULT, qos, timestamp, source_info, att); break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Send message on network if (_z_send_n_msg(zn, &z_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL) != _Z_RES_OK) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } // Freeing z_msg is unnecessary, as all of its components are aliased return _Z_RES_OK; } z_result_t _z_send_reply_err(const _z_query_t *query, const _z_session_rc_t *zsrc, _z_bytes_t *payload, _z_encoding_t *encoding) { z_result_t ret = _Z_RES_OK; _z_session_t *zn = _Z_RC_IN_VAL(zsrc); // Build the reply context decorator. This is NOT the final reply. _z_n_qos_t qos = _z_n_qos_make(false, true, Z_PRIORITY_DEFAULT); _z_source_info_t source_info = _z_source_info_null(); if (query->_is_local) { return _z_session_deliver_reply_err_locally(query, zsrc, payload, encoding, qos); } _z_zenoh_message_t msg; _z_n_msg_make_reply_err(&msg, &zn->_local_zid, query->_request_id, Z_RELIABILITY_DEFAULT, qos, payload, encoding, &source_info); // Send message on network if (_z_send_n_msg(zn, &msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL) != _Z_RES_OK) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; } return ret; } #endif #if Z_FEATURE_QUERY == 1 /*------------------ Querier Declaration ------------------*/ z_result_t _z_declare_querier(_z_querier_t *querier, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, z_consolidation_mode_t consolidation_mode, z_congestion_control_t congestion_control, z_query_target_t target, z_priority_t priority, bool is_express, uint64_t timeout_ms, _z_encoding_t *encoding, z_reliability_t reliability, z_locality_t allowed_destination, z_reply_keyexpr_t accept_replies) { *querier = _z_querier_null(); querier->_encoding = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); querier->reliability = reliability; querier->_id = _z_get_entity_id(_Z_RC_IN_VAL(zn)); querier->_consolidation_mode = consolidation_mode; querier->_congestion_control = congestion_control; querier->_target = target; querier->_priority = priority; querier->_is_express = is_express; querier->_accept_replies = accept_replies; querier->_timeout_ms = timeout_ms; querier->_allowed_destination = allowed_destination; querier->_zn = _z_session_rc_clone_as_weak(zn); querier->_filter = (_z_write_filter_t){0}; _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_declare(zn, &querier->_key, keyexpr), _z_undeclare_querier(querier)); return _Z_RES_OK; } z_result_t _z_undeclare_querier(_z_querier_t *querier) { if (querier == NULL || _Z_RC_IS_NULL(&querier->_zn)) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } _z_session_rc_t s = _z_session_weak_upgrade_if_open(&querier->_zn); if (!_Z_RC_IS_NULL(&s)) { _z_unregister_pending_queries_from_querier(_Z_RC_IN_VAL(&s), querier->_id); _z_session_rc_drop(&s); } _z_write_filter_clear(&querier->_filter); _z_declared_keyexpr_clear(&querier->_key); _z_session_weak_drop(&querier->_zn); _z_encoding_clear(&querier->_encoding); *querier = _z_querier_null(); return _Z_RES_OK; } /*------------------ Query ------------------*/ z_result_t _z_query(const _z_session_rc_t *session, _z_optional_id_t querier_id, const _z_declared_keyexpr_t *keyexpr, const char *parameters, size_t parameters_len, z_query_target_t target, z_consolidation_mode_t consolidation, _z_bytes_t *payload, _z_encoding_t *encoding, _z_closure_reply_callback_t callback, _z_drop_handler_t dropper, void *arg, uint64_t timeout_ms, _z_bytes_t *attachment, _z_n_qos_t qos, _z_source_info_t *source_info, z_reply_keyexpr_t accept_replies, z_locality_t allowed_destination, _z_cancellation_token_rc_t *opt_cancellation_token) { _z_session_t *zn = _Z_RC_IN_VAL(session); if (parameters == NULL && parameters_len > 0) { _Z_ERROR("Non-zero length string should not be NULL"); return Z_EINVAL; } bool allow_local = _z_locality_allows_local(allowed_destination); bool allow_remote = _z_locality_allows_remote(allowed_destination); size_t remote_targets = allow_remote ? _z_transport_get_peers_count(&zn->_tp) : 0; #if Z_FEATURE_LOCAL_QUERYABLE == 1 size_t remaining_finals = (allow_local ? 1 : 0) + remote_targets; #else _ZP_UNUSED(allow_local); size_t remaining_finals = remote_targets; #endif if (remaining_finals == 0) { _z_drop_handler_execute(dropper, arg); return _z_session_is_closed(zn) ? _Z_ERR_SESSION_CLOSED : _Z_RES_OK; } _z_keyexpr_t ke_query; _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_copy(&ke_query, &keyexpr->_inner), _z_drop_handler_execute(dropper, arg)); if (consolidation == Z_CONSOLIDATION_MODE_AUTO) { if (parameters != NULL && _z_strstr(parameters, parameters + parameters_len, Z_SELECTOR_TIME) != NULL) { consolidation = Z_CONSOLIDATION_MODE_NONE; } else { consolidation = Z_CONSOLIDATION_MODE_LATEST; } } bool _anyke_in_parameters = _z_parameters_has_anyke(parameters, parameters_len); bool _anyke_option = accept_replies == Z_REPLY_KEYEXPR_ANY; // extra _anyke parameter only if it's not already in the parameters list bool implicit_anyke = _anyke_option && !_anyke_in_parameters; // Add the pending query to the current session _z_zint_t qid; z_result_t ret = _Z_RES_OK; _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&ke_query); _z_drop_handler_execute(dropper, arg)); _z_pending_query_t *pq = _z_unsafe_register_pending_query(zn); if (pq == NULL) { _z_session_mutex_unlock(zn); _z_keyexpr_clear(&ke_query); _z_drop_handler_execute(dropper, arg); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } // Fill the pending query object qid = pq->_id; pq->_querier_id = querier_id; pq->_key = ke_query; pq->_target = target; pq->_consolidation = consolidation; pq->_anyke = _anyke_in_parameters || _anyke_option; pq->_callback = callback; pq->_dropper = dropper; pq->_pending_replies = NULL; pq->_allowed_destination = allowed_destination; pq->_arg = arg; pq->_timeout = timeout_ms; pq->_start_time = z_clock_now(); pq->_remaining_finals = (uint32_t)remaining_finals; #ifdef Z_FEATURE_UNSTABLE_API ret = _z_pending_query_register_cancellation(pq, opt_cancellation_token, session); #else _ZP_UNUSED(opt_cancellation_token); #endif _z_session_mutex_unlock(zn); // Send query message _z_slice_t params = (parameters == NULL) ? _z_slice_null() : _z_slice_alias_buf((uint8_t *)parameters, parameters_len); if (ret == _Z_RES_OK && remote_targets > 0) { _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_zenoh_message_t z_msg; _z_n_msg_make_query(&z_msg, &wireexpr, ¶ms, qid, Z_RELIABILITY_DEFAULT, consolidation, payload, encoding, timeout_ms, attachment, qos, source_info, implicit_anyke); ret = _z_send_n_msg(zn, &z_msg, Z_RELIABILITY_RELIABLE, _z_n_qos_get_congestion_control(qos), NULL); } #if Z_FEATURE_LOCAL_QUERYABLE == 1 if (ret == _Z_RES_OK && allow_local) { ret = _z_session_deliver_query_locally(zn, &keyexpr->_inner, ¶ms, consolidation, payload, encoding, attachment, source_info, qid, timeout_ms, qos, implicit_anyke); } #endif _Z_CLEAN_RETURN_IF_ERR(ret, _z_unregister_pending_query(zn, qid)); return _Z_RES_OK; } #endif #if Z_FEATURE_INTEREST == 1 /*------------------ Interest Declaration ------------------*/ uint32_t _z_add_interest(_z_session_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_interest_handler_t callback, uint8_t flags, _z_void_rc_t *arg) { _z_session_interest_t intr; intr._id = _z_get_entity_id(zn); intr._flags = flags; intr._callback = callback; intr._arg = *arg; *arg = _z_void_rc_null(); if (_z_keyexpr_copy(&intr._key, &keyexpr->_inner) != _Z_RES_OK) { _z_void_rc_drop(&intr._arg); return 0; } // Create interest entry, stored at session-level, do not drop it by the end of this function. _z_session_interest_rc_t *sintr = _z_register_interest(zn, &intr); if (sintr == NULL) { _z_void_rc_drop(&intr._arg); _z_keyexpr_clear(&intr._key); return 0; } // Build the interest message to send on the wire (only needed in client mode or multicast transport or when // connected to a router in peer mode) if (zn->_mode == Z_WHATAMI_CLIENT || _z_session_has_router_peer(zn) #if Z_FEATURE_MULTICAST_DECLARATIONS == 1 || (zn->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) #endif ) { _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, zn); _z_interest_t interest = _z_make_interest(&wireexpr, intr._id, intr._flags); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); if (_z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL) != _Z_RES_OK) { _z_unregister_interest(zn, sintr); return 0; } #if Z_FEATURE_AUTO_RECONNECT == 1 _z_cache_declaration(zn, &n_msg); #endif _z_n_msg_clear(&n_msg); } // Replay declares _z_interest_replay_declare(zn, &intr); return intr._id; } z_result_t _z_remove_interest(_z_session_t *zn, uint32_t interest_id) { // Find interest entry _z_session_interest_rc_t *sintr = _z_get_interest_by_id(zn, interest_id); if (sintr == NULL) { _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } // Build the declare message to send on the wire (only needed in client mode or multicast transport) if (zn->_mode == Z_WHATAMI_CLIENT #if Z_FEATURE_MULTICAST_DECLARATIONS == 1 || (zn->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) #endif ) { _z_interest_t interest = _z_make_interest_final(_Z_RC_IN_VAL(sintr)->_id); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); if (_z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL) != _Z_RES_OK) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } #if Z_FEATURE_AUTO_RECONNECT == 1 _z_prune_declaration(zn, &n_msg); #endif _z_n_msg_clear(&n_msg); } // Only if message is successfully send, session interest can be removed _z_unregister_interest(zn, sintr); return _Z_RES_OK; } #endif ================================================ FILE: src/net/query.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/query.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/session/loopback.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" static void _z_query_clear_inner(_z_query_t *q) { _z_declared_keyexpr_clear(&q->_key); _z_value_clear(&q->_value); _z_bytes_drop(&q->_attachment); _z_string_clear(&q->_parameters); _z_session_weak_drop(&q->_zn); } z_result_t _z_session_send_reply_final(_z_session_t *session, uint32_t query_id, bool is_local) { if (is_local) { return _z_session_deliver_reply_final_locally(session, query_id); } else { _z_zenoh_message_t z_msg; _z_n_msg_make_response_final(&z_msg, query_id); z_result_t ret = _z_send_n_msg(session, &z_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); _z_msg_clear(&z_msg); return ret; } } z_result_t _z_query_send_reply_final(_z_query_t *q) { _z_session_rc_t sess_rc = _z_session_weak_upgrade_if_open(&q->_zn); if (_Z_RC_IS_NULL(&sess_rc)) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } z_result_t ret = _z_session_send_reply_final(_Z_RC_IN_VAL(&sess_rc), q->_request_id, q->_is_local); _z_session_rc_drop(&sess_rc); return ret; } void _z_query_clear(_z_query_t *q) { // Send REPLY_FINAL message if (!_Z_RC_IS_NULL(&q->_zn) && _z_query_send_reply_final(q) != _Z_RES_OK) { _Z_ERROR("Query send REPLY_FINAL transport failure !"); } // Clean up memory _z_query_clear_inner(q); } void _z_query_free(_z_query_t **query) { _z_query_t *ptr = *query; if (ptr != NULL) { _z_query_clear(ptr); z_free(ptr); *query = NULL; } } #if Z_FEATURE_QUERYABLE == 1 void _z_queryable_clear(_z_queryable_t *qbl) { _z_session_weak_drop(&qbl->_zn); _z_sync_group_drop(&qbl->_callback_drop_sync_group); *qbl = _z_queryable_null(); } void _z_queryable_free(_z_queryable_t **qbl) { _z_queryable_t *ptr = *qbl; if (ptr != NULL) { _z_queryable_clear(ptr); z_free(ptr); *qbl = NULL; } } #endif ================================================ FILE: src/net/reply.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/reply.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_QUERY == 1 void _z_reply_data_clear(_z_reply_data_t *reply_data) { if (reply_data->_tag == _Z_REPLY_TAG_DATA) { _z_sample_clear(&reply_data->_result.sample); } else if (reply_data->_tag == _Z_REPLY_TAG_ERROR) { _z_value_clear(&reply_data->_result.error); } reply_data->_tag = _Z_REPLY_TAG_NONE; reply_data->replier_id = _z_entity_global_id_null(); } void _z_reply_data_free(_z_reply_data_t **reply_data) { _z_reply_data_t *ptr = *reply_data; if (ptr != NULL) { _z_reply_data_clear(ptr); z_free(ptr); *reply_data = NULL; } } z_result_t _z_reply_data_copy(_z_reply_data_t *dst, const _z_reply_data_t *src) { if (src->_tag == _Z_REPLY_TAG_DATA) { _Z_RETURN_IF_ERR(_z_sample_copy(&dst->_result.sample, &src->_result.sample)); } else if (src->_tag == _Z_REPLY_TAG_ERROR) { _Z_RETURN_IF_ERR(_z_value_copy(&dst->_result.error, &src->_result.error)); } dst->_tag = src->_tag; dst->replier_id = src->replier_id; return _Z_RES_OK; } void _z_reply_steal_data(_z_reply_t *dst, _z_keyexpr_t *keyexpr, _z_entity_global_id_t replier_id, _z_bytes_t *payload, const _z_timestamp_t *timestamp, _z_encoding_t *encoding, z_sample_kind_t kind, _z_bytes_t *attachment, _z_source_info_t *source_info) { dst->data.replier_id = replier_id; dst->data._tag = _Z_REPLY_TAG_DATA; _z_sample_steal_data(&dst->data._result.sample, keyexpr, payload, timestamp, encoding, kind, _Z_N_QOS_DEFAULT, attachment, Z_RELIABILITY_DEFAULT, source_info); } void _z_reply_err_steal_data(_z_reply_t *dst, _z_bytes_t *payload, _z_encoding_t *encoding, _z_entity_global_id_t replier_id) { dst->data.replier_id = replier_id; dst->data._tag = _Z_REPLY_TAG_ERROR; dst->data._result.error.payload = _z_bytes_steal(payload); dst->data._result.error.encoding = _z_encoding_steal(encoding); } z_result_t _z_reply_move(_z_reply_t *dst, _z_reply_t *src) { dst->data._tag = src->data._tag; dst->data.replier_id = src->data.replier_id; if (src->data._tag == _Z_REPLY_TAG_DATA) { _Z_RETURN_IF_ERR(_z_sample_move(&dst->data._result.sample, &src->data._result.sample)); } else if (src->data._tag == _Z_REPLY_TAG_ERROR) { _Z_RETURN_IF_ERR(_z_value_move(&dst->data._result.error, &src->data._result.error)); } *src = _z_reply_null(); return _Z_RES_OK; } void _z_reply_clear(_z_reply_t *reply) { _z_reply_data_clear(&reply->data); } void _z_reply_free(_z_reply_t **reply) { _z_reply_t *ptr = *reply; if (*reply != NULL) { _z_reply_clear(ptr); z_free(ptr); *reply = NULL; } } z_result_t _z_reply_copy(_z_reply_t *dst, const _z_reply_t *src) { return _z_reply_data_copy(&dst->data, &src->data); } bool _z_pending_reply_eq(const _z_pending_reply_t *one, const _z_pending_reply_t *two) { return one->_tstamp.time == two->_tstamp.time; } void _z_pending_reply_clear(_z_pending_reply_t *pr) { // Free reply _z_reply_clear(&pr->_reply); // Free the timestamp _z_timestamp_clear(&pr->_tstamp); } #endif // Z_FEATURE_QUERY == 1 ================================================ FILE: src/net/sample.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/sample.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/logging.h" void _z_sample_steal_data(_z_sample_t *dst, _z_keyexpr_t *key, _z_bytes_t *payload, const _z_timestamp_t *timestamp, _z_encoding_t *encoding, z_sample_kind_t kind, _z_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info) { *dst = _z_sample_null(); dst->keyexpr._inner = _z_keyexpr_steal(key); dst->payload = _z_bytes_steal(payload); dst->attachment = _z_bytes_steal(attachment); dst->encoding = _z_encoding_steal(encoding); dst->timestamp = *timestamp; dst->kind = kind; dst->qos = qos; dst->reliability = reliability; dst->source_info = *source_info; } z_result_t _z_sample_copy_data(_z_sample_t *dst, const _z_declared_keyexpr_t *key, const _z_bytes_t *payload, const _z_timestamp_t *timestamp, const _z_encoding_t *encoding, z_sample_kind_t kind, _z_qos_t qos, const _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info) { *dst = _z_sample_null(); _Z_RETURN_IF_ERR(_z_declared_keyexpr_copy(&dst->keyexpr, key)); _Z_CLEAN_RETURN_IF_ERR(_z_encoding_copy(&dst->encoding, encoding), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_copy(&dst->payload, payload), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_copy(&dst->attachment, attachment), _z_sample_clear(dst)); dst->timestamp = _z_timestamp_duplicate(timestamp); dst->source_info = *source_info; dst->qos = qos; dst->reliability = reliability; dst->kind = kind; return _Z_RES_OK; } z_result_t _z_sample_move(_z_sample_t *dst, _z_sample_t *src) { *dst = _z_sample_null(); _Z_RETURN_IF_ERR(_z_declared_keyexpr_move(&dst->keyexpr, &src->keyexpr)); _Z_CLEAN_RETURN_IF_ERR(_z_encoding_move(&dst->encoding, &src->encoding), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_move(&dst->payload, &src->payload), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_move(&dst->attachment, &src->attachment), _z_sample_clear(dst)); _z_timestamp_move(&dst->timestamp, &src->timestamp); dst->source_info = src->source_info; dst->qos = src->qos; dst->reliability = src->reliability; dst->kind = src->kind; return _Z_RES_OK; } void _z_sample_clear(_z_sample_t *sample) { _z_declared_keyexpr_clear(&sample->keyexpr); _z_encoding_clear(&sample->encoding); _z_bytes_drop(&sample->payload); _z_bytes_drop(&sample->attachment); } void _z_sample_free(_z_sample_t **sample) { _z_sample_t *ptr = *sample; if (ptr != NULL) { _z_sample_clear(ptr); z_free(ptr); *sample = NULL; } } z_result_t _z_sample_copy(_z_sample_t *dst, const _z_sample_t *src) { *dst = _z_sample_null(); _Z_RETURN_IF_ERR(_z_declared_keyexpr_copy(&dst->keyexpr, &src->keyexpr)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_copy(&dst->payload, &src->payload), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_encoding_copy(&dst->encoding, &src->encoding), _z_sample_clear(dst)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_copy(&dst->attachment, &src->attachment), _z_sample_clear(dst)); dst->kind = src->kind; dst->timestamp = _z_timestamp_duplicate(&src->timestamp); dst->source_info = src->source_info; dst->qos = src->qos; dst->reliability = src->reliability; return _Z_RES_OK; } _z_sample_t _z_sample_duplicate(const _z_sample_t *src) { _z_sample_t dst; _z_sample_copy(&dst, src); return dst; } ================================================ FILE: src/net/session.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include "zenoh-pico/net/session.h" #include #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/common/lease.h" #include "zenoh-pico/transport/common/read.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast.h" #include "zenoh-pico/transport/multicast/lease.h" #include "zenoh-pico/transport/multicast/read.h" #include "zenoh-pico/transport/raweth/read.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast.h" #include "zenoh-pico/transport/unicast/lease.h" #include "zenoh-pico/transport/unicast/read.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #include "zenoh-pico/utils/sleep.h" #include "zenoh-pico/utils/string.h" #include "zenoh-pico/utils/uuid.h" #if Z_FEATURE_SCOUTING == 1 static z_result_t _z_locators_by_scout(const _z_config_t *config, const _z_id_t *zid, _z_string_svec_t *locators) { z_result_t ret = _Z_RES_OK; char *opt_as_str = _z_config_get(config, Z_CONFIG_SCOUTING_WHAT_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_SCOUTING_WHAT_DEFAULT; } z_what_t what = strtol(opt_as_str, NULL, 10); opt_as_str = _z_config_get(config, Z_CONFIG_MULTICAST_LOCATOR_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_MULTICAST_LOCATOR_DEFAULT; } _z_string_t mcast_locator = _z_string_alias_str(opt_as_str); opt_as_str = _z_config_get(config, Z_CONFIG_SCOUTING_TIMEOUT_KEY); if (opt_as_str == NULL) { opt_as_str = (char *)Z_CONFIG_SCOUTING_TIMEOUT_DEFAULT; } uint32_t timeout = (uint32_t)strtoul(opt_as_str, NULL, 10); // Scout and return upon the first result _z_hello_slist_t *hellos = _z_scout_inner(what, *zid, &mcast_locator, timeout, true); if (hellos != NULL) { _z_hello_t *hello = _z_hello_slist_value(hellos); _z_string_svec_copy(locators, &hello->_locators, true); } _z_hello_slist_free(&hellos); return ret; } #else static z_result_t _z_locators_by_scout(const _z_config_t *config, const _z_id_t *zid, _z_string_svec_t *locators) { _ZP_UNUSED(config); _ZP_UNUSED(zid); _ZP_UNUSED(locators); _Z_ERROR("Cannot scout as Z_FEATURE_SCOUTING was deactivated"); _Z_ERROR_RETURN(_Z_ERR_SCOUT_NO_RESULTS); } #endif static z_result_t _z_locators_by_config(_z_config_t *config, _z_string_svec_t *listen_locators, _z_string_svec_t *connect_locators) { _Z_RETURN_IF_ERR(_z_config_get_all(config, listen_locators, Z_CONFIG_LISTEN_KEY)); _Z_RETURN_IF_ERR(_z_config_get_all(config, connect_locators, Z_CONFIG_CONNECT_KEY)); size_t listen_len = _z_string_svec_len(listen_locators); size_t connect_len = _z_string_svec_len(connect_locators); if ((listen_len == 0) && (connect_len == 0)) { return _Z_RES_OK; } #if Z_FEATURE_UNICAST_PEER == 0 if ((listen_len > 0) && (connect_len > 0)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif if (listen_len > 0) { _zp_config_insert(config, Z_CONFIG_MODE_KEY, Z_CONFIG_MODE_PEER); } return _Z_RES_OK; } static z_result_t _z_config_get_mode(const _z_config_t *config, z_whatami_t *mode) { z_result_t ret = _Z_RES_OK; char *s_mode = _z_config_get(config, Z_CONFIG_MODE_KEY); *mode = Z_WHATAMI_CLIENT; // By default, zenoh-pico will operate as a client if (s_mode != NULL) { if (_z_str_eq(s_mode, Z_CONFIG_MODE_CLIENT) == true) { *mode = Z_WHATAMI_CLIENT; } else if (_z_str_eq(s_mode, Z_CONFIG_MODE_PEER) == true) { *mode = Z_WHATAMI_PEER; } else { _Z_ERROR("Trying to configure an invalid mode: %s", s_mode); _Z_ERROR_LOG(_Z_ERR_CONFIG_INVALID_MODE); ret = _Z_ERR_CONFIG_INVALID_MODE; } } return ret; } static z_result_t _z_open_inner(_z_session_rc_t *zs, _z_string_t *locator, const _z_id_t *zid, int peer_op, const _z_config_t *config) { z_result_t ret = _Z_RES_OK; _z_session_t *zn = _Z_RC_IN_VAL(zs); ret = _z_new_transport(&zn->_tp, zid, locator, zn->_mode, peer_op, config, &_Z_RC_IN_VAL(zs)->_runtime); if (ret != _Z_RES_OK) { return ret; } _z_transport_get_common(&zn->_tp)->_session = _z_session_rc_clone_as_weak(zs); _z_transport_get_common(&zn->_tp)->_state = _Z_TRANSPORT_STATE_OPEN; #if Z_FEATURE_MULTICAST_DECLARATIONS == 1 if (zn->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) { ret = _z_interest_pull_resource_from_peers(zn); } #endif return ret; } static int32_t _z_get_remaining_timeout_ms(z_clock_t *start, int32_t timeout_ms) { if (timeout_ms < 0) { return -1; } if (timeout_ms == 0) { return 0; } unsigned long elapsed = z_clock_elapsed_ms(start); if (elapsed >= (unsigned long)timeout_ms) { return 0; } return timeout_ms - (int32_t)elapsed; } typedef struct { bool transport_opened; int32_t remaining_timeout_ms; } _z_open_connect_result_t; /* * Attempt connect locators that are still marked pending. * * The pending_peers array is both input and output: * - PENDING locators may still be attempted; * - a locator that succeeds as the primary transport is marked DONE; * - a locator that fails with a non-retryable error is marked FAILED; * - retryable failures remain PENDING for another attempt or later peer addition. * - if exit_on_non_retryable_failure is true, the first non-retryable error is returned immediately; * - retryable errors are governed by timeout/backoff and do not fail fast through this flag. * * On success, out reports that the primary transport is open and how much of the * original timeout remains. In peer mode, remaining PENDING locators are used to * decide which connect locators still need to be added as peers. */ static z_result_t _z_open_connect_locator(_z_session_rc_t *zn, _z_pending_peers_t *pending_peers, const _z_id_t *zid, _z_config_t *config, int32_t timeout_ms, bool exit_on_non_retryable_failure, _z_open_connect_result_t *out) { size_t connect_len = _z_pending_peer_svec_len(&pending_peers->_peers); z_result_t last_retryable_ret = _Z_ERR_TRANSPORT_OPEN_FAILED; z_result_t last_non_retryable_ret = _Z_RES_OK; out->transport_opened = false; out->remaining_timeout_ms = timeout_ms; if (connect_len == 0) { return _Z_ERR_CONFIG_LOCATOR_INVALID; } z_result_t ret = _Z_ERR_TRANSPORT_OPEN_FAILED; z_clock_t now = z_clock_now(); uint32_t sleep_ms = _Z_SLEEP_BACKOFF_MIN_MS; while (!out->transport_opened) { _Z_DEBUG("Attempting to open %zu connect locator(s)", connect_len); for (size_t i = 0; i < connect_len; i++) { _z_pending_peer_t *peer = _z_pending_peer_svec_get(&pending_peers->_peers, i); if (peer->_state != _Z_PENDING_PEER_STATE_PENDING) { continue; } _z_string_t *locator = &peer->_locator; ret = _z_open_inner(zn, locator, zid, _Z_PEER_OP_OPEN, config); if (ret == _Z_RES_OK) { out->transport_opened = true; peer->_state = _Z_PENDING_PEER_STATE_DONE; _Z_DEBUG("Successfully opened connect locator [%zu]: %.*s", i, (int)_z_string_len(locator), _z_string_data(locator)); out->remaining_timeout_ms = _z_get_remaining_timeout_ms(&now, timeout_ms); return _Z_RES_OK; } _Z_DEBUG("Failed to open connect locator [%zu]: %.*s (ret=%d)", i, (int)_z_string_len(locator), _z_string_data(locator), ret); if (_z_transport_open_error_is_retryable(ret)) { last_retryable_ret = ret; continue; } // Non-retryable error. last_non_retryable_ret = ret; peer->_state = _Z_PENDING_PEER_STATE_FAILED; if (exit_on_non_retryable_failure) { out->remaining_timeout_ms = _z_get_remaining_timeout_ms(&now, timeout_ms); return ret; } _Z_DEBUG("Removing connect locator [%zu] from pending set due to non-retryable error", i); } if (!_z_pending_peers_has_pending(pending_peers)) { break; } if (!_z_backoff_sleep(&now, timeout_ms, &sleep_ms)) { break; } _Z_DEBUG("Retrying connect locators"); } out->remaining_timeout_ms = _z_get_remaining_timeout_ms(&now, timeout_ms); if (last_non_retryable_ret != _Z_RES_OK) { return last_non_retryable_ret; } return last_retryable_ret; } z_result_t _z_open_locators_client(_z_session_rc_t *zn, const _z_string_svec_t *connect_locators, const _z_id_t *zid, _z_config_t *config, int32_t timeout_ms) { size_t connect_len = _z_string_svec_len(connect_locators); if (connect_len == 0) { _Z_ERROR("No connect locators configured in client mode"); return _Z_ERR_CONFIG_LOCATOR_INVALID; } _z_pending_peers_t pending_peers = _z_pending_peers_null(); _Z_RETURN_IF_ERR(_z_pending_peers_copy_from_locators(&pending_peers, connect_locators)); _z_open_connect_result_t connect_result; z_result_t ret = _z_open_connect_locator(zn, &pending_peers, zid, config, timeout_ms, false, &connect_result); _z_pending_peers_clear(&pending_peers); return ret; } z_result_t _z_open_bind_listener(_z_session_rc_t *zn, _z_string_t *locator, const _z_id_t *zid, _z_config_t *config, int32_t timeout_ms) { z_result_t ret = _Z_ERR_TRANSPORT_OPEN_FAILED; z_clock_t now = z_clock_now(); uint32_t sleep_ms = _Z_SLEEP_BACKOFF_MIN_MS; while (ret != _Z_RES_OK) { ret = _z_open_inner(zn, locator, zid, _Z_PEER_OP_LISTEN, config); if (ret == _Z_RES_OK) { return _Z_RES_OK; } if (!_z_transport_open_error_is_retryable(ret)) { break; } if (!_z_backoff_sleep(&now, timeout_ms, &sleep_ms)) { break; } } return ret; } z_result_t _z_open_locators_peer(_z_session_rc_t *zn, _z_string_t *listen_locator, const _z_string_svec_t *connect_locators, const _z_id_t *zid, _z_config_t *config, int32_t listen_timeout_ms, bool listen_exit_on_failure, int32_t connect_timeout_ms, bool connect_exit_on_failure) { size_t connect_len = _z_string_svec_len(connect_locators); bool transport_opened = false; #if Z_FEATURE_UNICAST_PEER == 0 if ((listen_locator != NULL && connect_len > 0) || (connect_len > 1)) { _Z_ERROR("Multiple connect locators, or combined listen and connect locators, require peer support"); return _Z_ERR_CONFIG_LOCATOR_INVALID; } #endif // First, try to open the optional listen locator. if (listen_locator != NULL) { z_result_t ret = _z_open_bind_listener(zn, listen_locator, zid, config, listen_timeout_ms); if (ret == _Z_RES_OK) { transport_opened = true; _Z_DEBUG("Successfully opened listen locator: %.*s", (int)_z_string_len(listen_locator), _z_string_data(listen_locator)); } else { _Z_DEBUG("Failed to open listen locator: %.*s (ret=%d)", (int)_z_string_len(listen_locator), _z_string_data(listen_locator), ret); if (listen_exit_on_failure) { return ret; } } } _z_pending_peers_t pending_peers = _z_pending_peers_null(); if (connect_len > 0) { _Z_RETURN_IF_ERR(_z_pending_peers_copy_from_locators(&pending_peers, connect_locators)); } int32_t remaining_timeout_ms = connect_timeout_ms; if (!transport_opened && (connect_len > 0)) { _z_open_connect_result_t connect_result; _Z_CLEAN_RETURN_IF_ERR(_z_open_connect_locator(zn, &pending_peers, zid, config, connect_timeout_ms, connect_exit_on_failure, &connect_result), _z_pending_peers_clear(&pending_peers)); transport_opened = connect_result.transport_opened; remaining_timeout_ms = connect_result.remaining_timeout_ms; } if (!transport_opened) { _Z_ERROR("Failed to establish primary transport via listen or connect locators"); _z_pending_peers_clear(&pending_peers); return _Z_ERR_TRANSPORT_OPEN_FAILED; } #if Z_FEATURE_UNICAST_PEER == 1 if (_z_pending_peers_has_pending(&pending_peers)) { pending_peers._timeout_ms = remaining_timeout_ms; pending_peers._start = z_clock_now(); pending_peers._sleep_ms = _Z_SLEEP_BACKOFF_MIN_MS; // Ownership of pending_peers is transferred to _z_add_peers() if background retries are needed. z_result_t ret = _z_add_peers(&_Z_RC_IN_VAL(zn)->_tp, zid, &pending_peers, config, connect_exit_on_failure); if (connect_exit_on_failure) { if (ret == _Z_ERR_TRANSPORT_OPEN_FAILED) { _z_pending_peers_clear(&pending_peers); return _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY; } _z_pending_peers_clear(&pending_peers); return ret; } } #endif _z_pending_peers_clear(&pending_peers); return _Z_RES_OK; } static inline z_result_t _z_validate_open_timeout(int32_t timeout_ms) { return (timeout_ms >= -1) ? _Z_RES_OK : _Z_ERR_CONFIG_INVALID_VALUE; } static inline const char *_z_open_connect_exit_on_failure_default(z_whatami_t mode) { return (mode == Z_WHATAMI_CLIENT) ? Z_CONFIG_CONNECT_EXIT_ON_FAILURE_CLIENT_DEFAULT : Z_CONFIG_CONNECT_EXIT_ON_FAILURE_PEER_DEFAULT; } z_result_t _z_open_locators(_z_session_rc_t *zn, const _z_string_svec_t *listen_locators, const _z_string_svec_t *connect_locators, const _z_id_t *zid, _z_config_t *config, z_whatami_t mode) { size_t listen_len = _z_string_svec_len(listen_locators); size_t connect_len = _z_string_svec_len(connect_locators); if ((listen_len == 0) && (connect_len == 0)) { _Z_ERROR("No listen or connect locators configured"); return _Z_ERR_CONFIG_LOCATOR_INVALID; } if (listen_len > 1) { _Z_ERROR("Multiple listen locators are not supported in zenoh-pico"); return _Z_ERR_CONFIG_LOCATOR_INVALID; } if ((mode == Z_WHATAMI_CLIENT) && (listen_len > 0)) { _Z_ERROR("Listen locators are not supported in client mode"); return _Z_ERR_CONFIG_LOCATOR_INVALID; } int32_t listen_timeout_ms; bool listen_exit_on_failure; int32_t connect_timeout_ms; bool connect_exit_on_failure; #if defined(Z_FEATURE_UNSTABLE_API) _Z_RETURN_IF_ERR(_z_config_get_i32_default(config, Z_CONFIG_LISTEN_TIMEOUT_KEY, Z_CONFIG_LISTEN_TIMEOUT_DEFAULT, &listen_timeout_ms)); _Z_RETURN_IF_ERR(_z_validate_open_timeout(listen_timeout_ms)); _Z_RETURN_IF_ERR(_z_config_get_bool_default(config, Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY, Z_CONFIG_LISTEN_EXIT_ON_FAILURE_DEFAULT, &listen_exit_on_failure)); _Z_RETURN_IF_ERR(_z_config_get_i32_default(config, Z_CONFIG_CONNECT_TIMEOUT_KEY, Z_CONFIG_CONNECT_TIMEOUT_DEFAULT, &connect_timeout_ms)); _Z_RETURN_IF_ERR(_z_validate_open_timeout(connect_timeout_ms)); const char *connect_exit_default = _z_open_connect_exit_on_failure_default(mode); _Z_RETURN_IF_ERR(_z_config_get_bool_default(config, Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY, connect_exit_default, &connect_exit_on_failure)); #else if (!_z_str_parse_i32(Z_CONFIG_LISTEN_TIMEOUT_DEFAULT, &listen_timeout_ms)) { return _Z_ERR_CONFIG_INVALID_VALUE; } _Z_RETURN_IF_ERR(_z_validate_open_timeout(listen_timeout_ms)); if (!_z_str_parse_bool(Z_CONFIG_LISTEN_EXIT_ON_FAILURE_DEFAULT, &listen_exit_on_failure)) { return _Z_ERR_CONFIG_INVALID_VALUE; } if (!_z_str_parse_i32(Z_CONFIG_CONNECT_TIMEOUT_DEFAULT, &connect_timeout_ms)) { return _Z_ERR_CONFIG_INVALID_VALUE; } _Z_RETURN_IF_ERR(_z_validate_open_timeout(connect_timeout_ms)); if (!_z_str_parse_bool(_z_open_connect_exit_on_failure_default(mode), &connect_exit_on_failure)) { return _Z_ERR_CONFIG_INVALID_VALUE; } #endif switch (mode) { case Z_WHATAMI_CLIENT: return _z_open_locators_client(zn, connect_locators, zid, config, connect_timeout_ms); case Z_WHATAMI_PEER: { _z_string_t *listen_locator = (listen_len == 1) ? _z_string_svec_get(listen_locators, 0) : NULL; return _z_open_locators_peer(zn, listen_locator, connect_locators, zid, config, listen_timeout_ms, listen_exit_on_failure, connect_timeout_ms, connect_exit_on_failure); } default: return _Z_ERR_CONFIG_INVALID_MODE; } } /** * Open transports based on the configured listen and connect locators. * * Semantics: * - A "primary transport" must be established before this function returns. * This is achieved by either: * - successfully binding a listen locator, or * - successfully opening one connect locator. * * - In peer mode: * - If a listen locator is provided, it is attempted first. * A successful bind satisfies the primary transport requirement. * - If no primary transport is established via listen, connect locators * are attempted in order, with retry/backoff applied to retryable failures. * * - In client mode: * - Only connect locators are used. * - Connect locators are alternatives and at least one must succeed. * * - Connect locator behaviour: * - Locators are attempted in sequence. * - Retryable failures are retried with exponential backoff until timeout. * - Non-retryable failures permanently exclude the locator from further attempts. * - If configured, a non-retryable failure may cause immediate exit. * * - Once a primary transport is established in peer mode: * - Remaining connect locators are treated as additional peers and may be added. * - Peer addition may retry synchronously or be continued by a background task, depending on configuration. * - If not all peers are added: * - either an error is returned, or * - partial connectivity is accepted, depending on configuration. * * - Timeout semantics: * - A timeout of 0 disables retries. * - A timeout of -1 allows infinite retry. * * Returns: * - _Z_RES_OK on success (primary transport established, and peer policy satisfied). * - An error if no primary transport could be established, or if peer policy requires * failure (e.g. exit-on-failure with incomplete connectivity). */ z_result_t _z_open(_z_session_rc_t *zn, _z_config_t *config, const _z_id_t *zid) { z_result_t ret = _Z_RES_OK; _Z_RC_IN_VAL(zn)->_tp._type = _Z_TRANSPORT_NONE; _z_string_svec_t listen_locators = _z_string_svec_null(); _z_string_svec_t connect_locators = _z_string_svec_null(); ret = _z_locators_by_config(config, &listen_locators, &connect_locators); if (ret == _Z_RES_OK) { z_whatami_t mode; ret = _z_config_get_mode(config, &mode); if (ret == _Z_RES_OK) { _Z_RC_IN_VAL(zn)->_mode = mode; if ((_z_string_svec_len(&listen_locators) > 0) || (_z_string_svec_len(&connect_locators) > 0)) { ret = _z_open_locators(zn, &listen_locators, &connect_locators, zid, config, mode); } else { ret = _z_locators_by_scout(config, zid, &connect_locators); if (ret == _Z_RES_OK) { if (_z_string_svec_len(&connect_locators) == 0) { ret = _Z_ERR_SCOUT_NO_RESULTS; } else { ret = _z_open_locators(zn, &listen_locators, &connect_locators, zid, config, mode); } } } } } _z_string_svec_clear(&listen_locators); _z_string_svec_clear(&connect_locators); return ret; } #if Z_FEATURE_AUTO_RECONNECT == 1 void _z_client_reopen_task_drop(void *ztc_arg) { _z_transport_common_t *tc = (_z_transport_common_t *)ztc_arg; if (tc->_state == _Z_TRANSPORT_STATE_RECONNECTING) { // Drop the weak session reference as the task is being dropped in the middle of reconnection. _z_session_weak_drop(&tc->_session); tc->_state = _Z_TRANSPORT_STATE_CLOSED; } } _z_fut_fn_result_t _z_client_reopen_task_fn(void *ztc_arg, _z_executor_t *executor) { _z_transport_common_t *tc = (_z_transport_common_t *)ztc_arg; _z_transport_tasks_t tasks_handles = tc->_tasks; _z_session_rc_t zs = _z_session_weak_upgrade(&tc->_session); // should not fail _z_session_t *s = _Z_RC_IN_VAL(&zs); _z_session_weak_drop(&tc->_session); if (_z_config_is_empty(&s->_config)) { _z_session_rc_drop(&zs); return _z_fut_fn_result_ready(); } _z_session_transport_mutex_lock(s); z_result_t ret = _z_open(&zs, &s->_config, &s->_local_zid); _z_session_transport_mutex_unlock(s); if (ret != _Z_RES_OK) { if (ret == _Z_ERR_TRANSPORT_OPEN_FAILED || ret == _Z_ERR_SCOUT_NO_RESULTS || ret == _Z_ERR_TRANSPORT_TX_FAILED || ret == _Z_ERR_TRANSPORT_RX_FAILED || ret == _Z_ERR_TRANSPORT_RX_DURATION_EXPIRED || ret == _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY) { _Z_DEBUG("Reopen failed, next try in 1s"); tc->_session = _z_session_rc_clone_as_weak(&zs); tc->_state = _Z_TRANSPORT_STATE_RECONNECTING; tc->_tasks = tasks_handles; _z_session_rc_drop(&zs); return _z_fut_fn_result_wake_up_after(1000); } else { _Z_ERROR("Reopen failed, will not retry"); tc->_state = _Z_TRANSPORT_STATE_CLOSED; _z_session_rc_drop(&zs); return _z_fut_fn_result_ready(); } } tc->_tasks = tasks_handles; if (!_z_network_message_slist_is_empty(s->_declaration_cache)) { _z_network_message_slist_t *iter = s->_declaration_cache; while (iter != NULL) { _z_network_message_t *n_msg = _z_network_message_slist_value(iter); ret = _z_send_n_msg(s, n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); if (ret != _Z_RES_OK) { _Z_DEBUG("Send message during reopen failed: %i", ret); _z_transport_clear(&s->_tp); tc->_session = _z_session_rc_clone_as_weak(&zs); tc->_state = _Z_TRANSPORT_STATE_RECONNECTING; _z_session_rc_drop(&zs); return _z_fut_fn_result_continue(); } iter = _z_network_message_slist_next(iter); } } _z_session_rc_drop(&zs); _Z_DEBUG("Reconnected successfully"); // Resume all sibling tasks that suspended themselves while waiting for reconnection. for (size_t i = 0; i < _Z_TRANSPORT_TASK_COUNT; i++) { _z_executor_resume_suspended_fut(executor, &tc->_tasks._task_handles[i]); } return _z_fut_fn_result_ready(); } void _z_cache_declaration(_z_session_t *zs, const _z_network_message_t *n_msg) { if (_z_config_is_empty(&zs->_config)) { return; } zs->_declaration_cache = _z_network_message_slist_push_back(zs->_declaration_cache, n_msg); } #define _Z_CACHE_DECLARATION_UNDECLARE_FILTER(tp) \ static bool _z_cache_declaration_undeclare_filter_##tp(const _z_network_message_t *left, \ const _z_network_message_t *right) { \ return left->_tag == _Z_N_DECLARE && right->_tag == _Z_N_DECLARE && \ left->_body._declare._decl._body._undecl_##tp._id == right->_body._declare._decl._body._decl_##tp._id; \ } _Z_CACHE_DECLARATION_UNDECLARE_FILTER(kexpr) _Z_CACHE_DECLARATION_UNDECLARE_FILTER(subscriber) _Z_CACHE_DECLARATION_UNDECLARE_FILTER(queryable) _Z_CACHE_DECLARATION_UNDECLARE_FILTER(token) static bool _z_cache_declaration_undeclare_filter_interest(const _z_network_message_t *left, const _z_network_message_t *right) { return left->_tag == _Z_N_INTEREST && right->_tag == _Z_N_INTEREST && left->_body._interest._interest._id == right->_body._interest._interest._id; } void _z_prune_declaration(_z_session_t *zs, const _z_network_message_t *n_msg) { #ifdef Z_BUILD_DEBUG size_t cnt_before = _z_network_message_slist_len(zs->_declaration_cache); #endif switch (n_msg->_tag) { case _Z_N_DECLARE: { const _z_declaration_t *decl = &n_msg->_body._declare._decl; switch (decl->_tag) { case _Z_UNDECL_KEXPR: zs->_declaration_cache = _z_network_message_slist_drop_first_filter( zs->_declaration_cache, _z_cache_declaration_undeclare_filter_kexpr, n_msg); break; case _Z_UNDECL_SUBSCRIBER: zs->_declaration_cache = _z_network_message_slist_drop_first_filter( zs->_declaration_cache, _z_cache_declaration_undeclare_filter_subscriber, n_msg); break; case _Z_UNDECL_QUERYABLE: zs->_declaration_cache = _z_network_message_slist_drop_first_filter( zs->_declaration_cache, _z_cache_declaration_undeclare_filter_queryable, n_msg); break; case _Z_UNDECL_TOKEN: zs->_declaration_cache = _z_network_message_slist_drop_first_filter( zs->_declaration_cache, _z_cache_declaration_undeclare_filter_token, n_msg); break; default: _Z_ERROR("Invalid decl for _z_prune_declaration: %i", decl->_tag); }; break; } case _Z_N_INTEREST: zs->_declaration_cache = _z_network_message_slist_drop_first_filter( zs->_declaration_cache, _z_cache_declaration_undeclare_filter_interest, n_msg); break; default: _Z_ERROR("Invalid net message for _z_prune_declaration: %i", n_msg->_tag); return; }; #ifdef Z_BUILD_DEBUG size_t cnt_after = _z_network_message_slist_len(zs->_declaration_cache); assert(cnt_before == cnt_after + 1); #endif } #endif // Z_FEATURE_AUTO_RECONNECT == 1 bool _z_session_is_closed(const _z_session_t *session) { return _z_atomic_bool_load((_z_atomic_bool_t *)&session->_is_closed, _z_memory_order_acquire); } bool _z_session_has_router_peer(const _z_session_t *session) { if (session->_tp._type == _Z_TRANSPORT_UNICAST_TYPE) { _z_transport_peer_unicast_slist_t *peers = session->_tp._transport._unicast._peers; while (peers != NULL) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(peers); if (peer->common._remote_whatami == Z_WHATAMI_ROUTER) { return true; } peers = _z_transport_peer_unicast_slist_next(peers); } } else if (session->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) { _z_transport_peer_multicast_slist_t *peers = session->_tp._transport._multicast._peers; while (peers != NULL) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(peers); if (peer->common._remote_whatami == Z_WHATAMI_ROUTER) { return true; } peers = _z_transport_peer_multicast_slist_next(peers); } } return false; } _z_session_rc_t _z_session_weak_upgrade_if_open(const _z_session_weak_t *weak) { _z_session_rc_t sess_rc = _z_session_weak_upgrade(weak); if (!_Z_RC_IS_NULL(&sess_rc) && _z_session_is_closed(_Z_RC_IN_VAL(&sess_rc))) { _z_session_rc_drop(&sess_rc); } return sess_rc; } _z_config_t *_z_info(const _z_session_t *zn) { _z_config_t *ps = (_z_config_t *)z_malloc(sizeof(_z_config_t)); if (ps != NULL) { _z_config_init(ps); _z_string_t s = _z_id_to_string(&zn->_local_zid); _zp_config_insert_string(ps, Z_INFO_PID_KEY, &s); _z_string_clear(&s); switch (zn->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: _zp_unicast_info_session(&zn->_tp, ps, zn->_mode); break; case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: _zp_multicast_info_session(&zn->_tp, ps); break; default: break; } } return ps; } z_result_t _zp_read(_z_session_t *zn, bool single_read) { return _z_read(&zn->_tp, single_read); } z_result_t _zp_send_keep_alive(_z_session_t *zn) { return _z_send_keep_alive(&zn->_tp); } z_result_t _zp_send_join(_z_session_t *zn) { return _z_send_join(&zn->_tp); } z_result_t _zp_start_transport_tasks(_z_session_t *zn) { switch (zn->_tp._type) { #if Z_FEATURE_UNICAST_TRANSPORT == 1 case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_common_t *tc = &zn->_tp._transport._unicast._common; // Order must match _Z_TRANSPORT_TASK_* index constants. _z_fut_fn_t tasks[_Z_TRANSPORT_TASK_COUNT] = {0}; tasks[_Z_TRANSPORT_TASK_KEEP_ALIVE] = _zp_unicast_keep_alive_task_fn; tasks[_Z_TRANSPORT_TASK_LEASE] = _zp_unicast_lease_task_fn; tasks[_Z_TRANSPORT_TASK_READ] = _zp_unicast_read_task_fn; #if Z_FEATURE_UNICAST_PEER == 1 tasks[_Z_TRANSPORT_TASK_ADD_PEERS] = _zp_add_peers_task_fn; #endif for (size_t i = 0; i < _ZP_ARRAY_SIZE(tasks); i++) { if (tasks[i] == NULL) continue; _z_fut_t f = _z_fut_null(); f._fut_arg = &zn->_tp._transport._unicast; f._fut_fn = tasks[i]; _z_fut_handle_t h = _z_runtime_spawn(&zn->_runtime, &f); if (_z_fut_handle_is_null(h)) { _Z_ERROR_RETURN(_Z_ERR_FAILED_TO_SPAWN_TASK); } #if Z_FEATURE_AUTO_RECONNECT == 1 tc->_tasks._task_handles[i] = h; #endif } break; } #endif #if Z_FEATURE_MULTICAST_TRANSPORT == 1 case _Z_TRANSPORT_MULTICAST_TYPE: { _z_transport_common_t *tc = &zn->_tp._transport._multicast._common; // Order must match _Z_TRANSPORT_TASK_* index constants. _z_fut_fn_t tasks[_Z_TRANSPORT_TASK_COUNT] = {0}; tasks[_Z_TRANSPORT_TASK_KEEP_ALIVE] = _zp_multicast_keep_alive_task_fn; tasks[_Z_TRANSPORT_TASK_LEASE] = _zp_multicast_lease_task_fn; tasks[_Z_TRANSPORT_TASK_READ] = _zp_multicast_read_task_fn; tasks[_Z_TRANSPORT_TASK_SEND_JOIN] = _zp_multicast_send_join_task_fn; for (size_t i = 0; i < _ZP_ARRAY_SIZE(tasks); i++) { if (tasks[i] == NULL) continue; _z_fut_t f = _z_fut_null(); f._fut_arg = &zn->_tp._transport._multicast; f._fut_fn = tasks[i]; _z_fut_handle_t h = _z_runtime_spawn(&zn->_runtime, &f); if (_z_fut_handle_is_null(h)) { _Z_ERROR_RETURN(_Z_ERR_FAILED_TO_SPAWN_TASK); } #if Z_FEATURE_AUTO_RECONNECT == 1 tc->_tasks._task_handles[i] = h; #endif } break; } #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_TRANSPORT_RAWETH_TYPE: { _z_transport_common_t *tc = &zn->_tp._transport._raweth._common; // Order must match _Z_TRANSPORT_TASK_* index constants. _z_fut_fn_t tasks[_Z_TRANSPORT_TASK_COUNT] = {0}; tasks[_Z_TRANSPORT_TASK_KEEP_ALIVE] = _zp_multicast_keep_alive_task_fn; tasks[_Z_TRANSPORT_TASK_LEASE] = _zp_multicast_lease_task_fn; tasks[_Z_TRANSPORT_TASK_READ] = _zp_raweth_read_task_fn; tasks[_Z_TRANSPORT_TASK_SEND_JOIN] = _zp_multicast_send_join_task_fn; for (size_t i = 0; i < _ZP_ARRAY_SIZE(tasks); i++) { if (tasks[i] == NULL) continue; _z_fut_t f = _z_fut_null(); f._fut_arg = &zn->_tp._transport._raweth; f._fut_fn = tasks[i]; _z_fut_handle_t h = _z_runtime_spawn(&zn->_runtime, &f); if (_z_fut_handle_is_null(h)) { _Z_ERROR_RETURN(_Z_ERR_FAILED_TO_SPAWN_TASK); } #if Z_FEATURE_AUTO_RECONNECT == 1 tc->_tasks._task_handles[i] = h; #endif } break; } #endif default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); return _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return _Z_RES_OK; } ================================================ FILE: src/net/subscribe.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/net/subscribe.h" #if Z_FEATURE_SUBSCRIPTION == 1 void _z_subscriber_clear(_z_subscriber_t *sub) { _z_session_weak_drop(&sub->_zn); _z_sync_group_drop(&sub->_callback_drop_sync_group); *sub = _z_subscriber_null(); } void _z_subscriber_free(_z_subscriber_t **sub) { _z_subscriber_t *ptr = *sub; if (ptr != NULL) { _z_subscriber_clear(ptr); z_free(ptr); *sub = NULL; } } #endif ================================================ FILE: src/protocol/codec/core.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/iobuf.h" z_result_t _z_zbuf_read_exact(_z_zbuf_t *zbf, uint8_t *dest, size_t length) { if (length > _z_zbuf_len(zbf)) { _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } _z_zbuf_read_bytes(zbf, dest, 0, length); return _Z_RES_OK; } ================================================ FILE: src/protocol/codec/declarations.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/declarations.h" #include #include #include #include #include "zenoh-pico/protocol/codec.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" // Placeholder value for extension decode #define _Z_KEYEXPR_MAPPING_UNKNOWN_REMOTE (uintptr_t)(&empty_id) z_result_t _z_decl_ext_keyexpr_encode(_z_wbuf_t *wbf, const _z_wireexpr_t *ke, bool has_next_ext) { uint8_t header = _Z_MSG_EXT_ENC_ZBUF | _Z_MSG_EXT_FLAG_M | 0x0f | (has_next_ext ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); uint32_t kelen = (uint32_t)(_z_wireexpr_has_suffix(ke) ? _z_string_len(&ke->_suffix) : 0); header = (uint8_t)((_z_wireexpr_is_local(ke) ? 2 : 0) | (kelen != 0 ? 1 : 0)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, 1 + kelen + _z_zint_len(ke->_id))); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, ke->_id)); if (kelen) { _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, (const uint8_t *)_z_string_data(&ke->_suffix), 0, kelen)) } return _Z_RES_OK; } z_result_t _z_decl_kexpr_encode(_z_wbuf_t *wbf, const _z_decl_kexpr_t *decl) { uint8_t header = _Z_DECL_KEXPR_MID; int has_kesuffix = _z_wireexpr_has_suffix(&decl->_keyexpr); if (has_kesuffix) { header |= _Z_DECL_KEXPR_FLAG_N; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, decl->_id)); _Z_RETURN_IF_ERR(_z_wireexpr_encode(wbf, has_kesuffix, &decl->_keyexpr)) return _Z_RES_OK; } z_result_t _z_decl_commons_encode(_z_wbuf_t *wbf, uint8_t header, bool has_extensions, uint32_t id, const _z_wireexpr_t *keyexpr) { bool has_kesuffix = _z_wireexpr_has_suffix(keyexpr); if (has_extensions) { header |= _Z_FLAG_Z_Z; } if (has_kesuffix) { header |= _Z_DECL_SUBSCRIBER_FLAG_N; } if (_z_wireexpr_is_local(keyexpr)) { header |= _Z_DECL_SUBSCRIBER_FLAG_M; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, id)); return _z_wireexpr_encode(wbf, has_kesuffix, keyexpr); } z_result_t _z_decl_subscriber_encode(_z_wbuf_t *wbf, const _z_decl_subscriber_t *decl) { uint8_t header = _Z_DECL_SUBSCRIBER_MID; _Z_RETURN_IF_ERR(_z_decl_commons_encode(wbf, header, false, decl->_id, &decl->_keyexpr)); return _Z_RES_OK; } z_result_t _z_undecl_kexpr_encode(_z_wbuf_t *wbf, const _z_undecl_kexpr_t *decl) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_UNDECL_KEXPR)); return _z_zsize_encode(wbf, decl->_id); } z_result_t _z_undecl_encode(_z_wbuf_t *wbf, uint8_t header, _z_zint_t decl_id, const _z_wireexpr_t *ke) { bool has_keyexpr_ext = _z_wireexpr_check(ke); if (has_keyexpr_ext) { header |= _Z_FLAG_Z_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, decl_id)); if (has_keyexpr_ext) { _Z_RETURN_IF_ERR(_z_decl_ext_keyexpr_encode(wbf, ke, false)); } return _Z_RES_OK; } z_result_t _z_undecl_subscriber_encode(_z_wbuf_t *wbf, const _z_undecl_subscriber_t *decl) { return _z_undecl_encode(wbf, _Z_UNDECL_SUBSCRIBER_MID, decl->_id, &decl->_ext_keyexpr); } z_result_t _z_decl_queryable_encode(_z_wbuf_t *wbf, const _z_decl_queryable_t *decl) { uint8_t header = _Z_DECL_QUERYABLE_MID; bool has_info_ext = decl->_ext_queryable_info._complete || (decl->_ext_queryable_info._distance != 0); _Z_RETURN_IF_ERR(_z_decl_commons_encode(wbf, header, has_info_ext, decl->_id, &decl->_keyexpr)); if (has_info_ext) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ENC_ZINT | 0x01)); uint8_t flags = 0; if (decl->_ext_queryable_info._complete) { flags |= 0x01; } uint64_t value = (uint64_t)flags | (uint64_t)decl->_ext_queryable_info._distance << 8; _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, value)); } return _Z_RES_OK; } z_result_t _z_undecl_queryable_encode(_z_wbuf_t *wbf, const _z_undecl_queryable_t *decl) { return _z_undecl_encode(wbf, _Z_UNDECL_QUERYABLE_MID, decl->_id, &decl->_ext_keyexpr); } z_result_t _z_decl_token_encode(_z_wbuf_t *wbf, const _z_decl_token_t *decl) { uint8_t header = _Z_DECL_TOKEN_MID; _Z_RETURN_IF_ERR(_z_decl_commons_encode(wbf, header, false, decl->_id, &decl->_keyexpr)); return _Z_RES_OK; } z_result_t _z_undecl_token_encode(_z_wbuf_t *wbf, const _z_undecl_token_t *decl) { return _z_undecl_encode(wbf, _Z_UNDECL_TOKEN_MID, decl->_id, &decl->_ext_keyexpr); } z_result_t _z_decl_final_encode(_z_wbuf_t *wbf) { uint8_t header = _Z_DECL_FINAL_MID; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); return _Z_RES_OK; } z_result_t _z_declaration_encode(_z_wbuf_t *wbf, const _z_declaration_t *decl) { z_result_t ret = _Z_RES_OK; switch (decl->_tag) { case _Z_DECL_KEXPR: { ret = _z_decl_kexpr_encode(wbf, &decl->_body._decl_kexpr); } break; case _Z_UNDECL_KEXPR: { ret = _z_undecl_kexpr_encode(wbf, &decl->_body._undecl_kexpr); } break; #if Z_FEATURE_SUBSCRIPTION == 1 case _Z_DECL_SUBSCRIBER: { ret = _z_decl_subscriber_encode(wbf, &decl->_body._decl_subscriber); } break; case _Z_UNDECL_SUBSCRIBER: { ret = _z_undecl_subscriber_encode(wbf, &decl->_body._undecl_subscriber); } break; #endif #if Z_FEATURE_QUERYABLE == 1 case _Z_DECL_QUERYABLE: { ret = _z_decl_queryable_encode(wbf, &decl->_body._decl_queryable); } break; case _Z_UNDECL_QUERYABLE: { ret = _z_undecl_queryable_encode(wbf, &decl->_body._undecl_queryable); } break; #endif #if Z_FEATURE_LIVELINESS == 1 case _Z_DECL_TOKEN: { ret = _z_decl_token_encode(wbf, &decl->_body._decl_token); } break; case _Z_UNDECL_TOKEN: { ret = _z_undecl_token_encode(wbf, &decl->_body._undecl_token); } break; #endif case _Z_DECL_FINAL: { ret = _z_decl_final_encode(wbf); } break; default: _Z_ERROR_LOG(_Z_ERR_MESSAGE_SERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_SERIALIZATION_FAILED; break; } return ret; } z_result_t _z_decl_kexpr_decode(_z_decl_kexpr_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { *decl = _z_decl_kexpr_null(); _Z_RETURN_IF_ERR(_z_zint16_decode(&decl->_id, zbf)); _Z_RETURN_IF_ERR( _z_wireexpr_decode(&decl->_keyexpr, zbf, _Z_HAS_FLAG(header, _Z_DECL_KEXPR_FLAG_N), true, mapping)); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x15)); } return _Z_RES_OK; } z_result_t _z_undecl_kexpr_decode(_z_undecl_kexpr_t *decl, _z_zbuf_t *zbf, uint8_t header) { *decl = _z_undecl_kexpr_null(); _Z_RETURN_IF_ERR(_z_zint16_decode(&decl->_id, zbf)); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x10)); } return _Z_RES_OK; } z_result_t _z_undecl_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_wireexpr_t *ke = (_z_wireexpr_t *)ctx; switch (extension->_header) { case _Z_MSG_EXT_ENC_ZBUF | _Z_MSG_EXT_FLAG_M | 0x0f: { _z_zbuf_t _zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); _z_zbuf_t *zbf = &_zbf; uint8_t header; _Z_RETURN_IF_ERR(_z_uint8_decode(&header, zbf)); uintptr_t mapping = _Z_HAS_FLAG(header, 2) ? _Z_KEYEXPR_MAPPING_UNKNOWN_REMOTE : _Z_KEYEXPR_MAPPING_LOCAL; _Z_RETURN_IF_ERR(_z_zint16_decode(&ke->_id, zbf)); if (_Z_HAS_FLAG(header, 1)) { size_t len = _z_zbuf_len(zbf); ke->_suffix = _z_string_preallocate(len); if (!_z_wireexpr_has_suffix(ke)) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_zbuf_read_bytes(zbf, (uint8_t *)_z_string_data(&ke->_suffix), 0, len); } ke->_mapping = mapping; } break; default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { return _z_msg_ext_unknown_error(extension, 0x0e); } } return _Z_RES_OK; } z_result_t _z_undecl_trivial_decode(_z_zbuf_t *zbf, _z_wireexpr_t *_ext_keyexpr, uint32_t *decl_id, uint8_t header, uintptr_t mapping) { _Z_RETURN_IF_ERR(_z_zint32_decode(decl_id, zbf)); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_undecl_decode_extensions, _ext_keyexpr)); if (_ext_keyexpr->_mapping == _Z_KEYEXPR_MAPPING_UNKNOWN_REMOTE) { _ext_keyexpr->_mapping = mapping; } } return _Z_RES_OK; } static z_result_t _z_decl_commons_decode(_z_zbuf_t *zbf, uint8_t header, bool *has_extensions, uint32_t *id, _z_wireexpr_t *ke, uintptr_t mapping) { *has_extensions = _Z_HAS_FLAG(header, _Z_FLAG_Z_Z); _Z_RETURN_IF_ERR(_z_zint32_decode(id, zbf)); _Z_RETURN_IF_ERR(_z_wireexpr_decode(ke, zbf, _Z_HAS_FLAG(header, _Z_DECL_SUBSCRIBER_FLAG_N), _Z_HAS_FLAG(header, _Z_DECL_SUBSCRIBER_FLAG_M), mapping)); return _Z_RES_OK; } z_result_t _z_decl_subscriber_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _ZP_UNUSED(ctx); switch (extension->_header) { default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { return _z_msg_ext_unknown_error(extension, 0x14); } } return _Z_RES_OK; } z_result_t _z_decl_subscriber_decode(_z_decl_subscriber_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { bool has_ext; *decl = _z_decl_subscriber_null(); _Z_RETURN_IF_ERR(_z_decl_commons_decode(zbf, header, &has_ext, &decl->_id, &decl->_keyexpr, mapping)); if (has_ext) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_decl_subscriber_decode_extensions, decl)); } return _Z_RES_OK; } z_result_t _z_undecl_subscriber_decode(_z_undecl_subscriber_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { *decl = _z_undecl_subscriber_null(); return _z_undecl_trivial_decode(zbf, &decl->_ext_keyexpr, &decl->_id, header, mapping); } z_result_t _z_decl_queryable_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_decl_queryable_t *decl = (_z_decl_queryable_t *)ctx; switch (extension->_header) { case _Z_MSG_EXT_ENC_ZINT | 0x01: { uint64_t val = extension->_body._zint._val; decl->_ext_queryable_info._complete = _Z_HAS_FLAG(val, 0x01); decl->_ext_queryable_info._distance = (uint16_t)(val >> 8); } break; default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { return _z_msg_ext_unknown_error(extension, 0x11); } } return _Z_RES_OK; } z_result_t _z_decl_queryable_decode(_z_decl_queryable_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { bool has_ext; *decl = _z_decl_queryable_null(); _Z_RETURN_IF_ERR(_z_decl_commons_decode(zbf, header, &has_ext, &decl->_id, &decl->_keyexpr, mapping)); if (has_ext) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_decl_queryable_decode_extensions, decl)); } return _Z_RES_OK; } z_result_t _z_undecl_queryable_decode(_z_undecl_queryable_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { *decl = _z_undecl_queryable_null(); return _z_undecl_trivial_decode(zbf, &decl->_ext_keyexpr, &decl->_id, header, mapping); } z_result_t _z_decl_token_decode(_z_decl_token_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { bool has_ext; *decl = _z_decl_token_null(); _Z_RETURN_IF_ERR(_z_decl_commons_decode(zbf, header, &has_ext, &decl->_id, &decl->_keyexpr, mapping)); if (has_ext) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x12)); } return _Z_RES_OK; } z_result_t _z_undecl_token_decode(_z_undecl_token_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { return _z_undecl_trivial_decode(zbf, &decl->_ext_keyexpr, &decl->_id, header, mapping); } z_result_t _z_decl_final_decode(_z_decl_final_t *decl, _z_zbuf_t *zbf, uint8_t header) { // Nothing to do _ZP_UNUSED(decl); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x13)); } return _Z_RES_OK; } z_result_t _z_declaration_decode(_z_declaration_t *decl, _z_zbuf_t *zbf, uintptr_t mapping) { uint8_t header; _Z_RETURN_IF_ERR(_z_uint8_decode(&header, zbf)); z_result_t ret; switch (_Z_MID(header)) { case _Z_DECL_KEXPR_MID: { decl->_tag = _Z_DECL_KEXPR; ret = _z_decl_kexpr_decode(&decl->_body._decl_kexpr, zbf, header, mapping); } break; case _Z_UNDECL_KEXPR_MID: { decl->_tag = _Z_UNDECL_KEXPR; ret = _z_undecl_kexpr_decode(&decl->_body._undecl_kexpr, zbf, header); } break; case _Z_DECL_SUBSCRIBER_MID: { decl->_tag = _Z_DECL_SUBSCRIBER; ret = _z_decl_subscriber_decode(&decl->_body._decl_subscriber, zbf, header, mapping); } break; case _Z_UNDECL_SUBSCRIBER_MID: { decl->_tag = _Z_UNDECL_SUBSCRIBER; ret = _z_undecl_subscriber_decode(&decl->_body._undecl_subscriber, zbf, header, mapping); } break; case _Z_DECL_QUERYABLE_MID: { decl->_tag = _Z_DECL_QUERYABLE; ret = _z_decl_queryable_decode(&decl->_body._decl_queryable, zbf, header, mapping); } break; case _Z_UNDECL_QUERYABLE_MID: { decl->_tag = _Z_UNDECL_QUERYABLE; ret = _z_undecl_queryable_decode(&decl->_body._undecl_queryable, zbf, header, mapping); } break; case _Z_DECL_TOKEN_MID: { decl->_tag = _Z_DECL_TOKEN; ret = _z_decl_token_decode(&decl->_body._decl_token, zbf, header, mapping); } break; case _Z_UNDECL_TOKEN_MID: { decl->_tag = _Z_UNDECL_TOKEN; ret = _z_undecl_token_decode(&decl->_body._undecl_token, zbf, header, mapping); } break; case _Z_DECL_FINAL_MID: { decl->_tag = _Z_DECL_FINAL; ret = _z_decl_final_decode(&decl->_body._decl_final, zbf, header); } break; default: { _Z_INFO("Unknown token type"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } return ret; } ================================================ FILE: src/protocol/codec/ext.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/ext.h" #include #include #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/codec.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" z_result_t _z_msg_ext_encode_unit(_z_wbuf_t *wbf, const _z_msg_ext_unit_t *ext) { z_result_t ret = _Z_RES_OK; (void)(wbf); (void)(ext); return ret; } z_result_t _z_msg_ext_decode_unit(_z_msg_ext_unit_t *ext, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; (void)(zbf); (void)(ext); return ret; } z_result_t _z_msg_ext_decode_unit_na(_z_msg_ext_unit_t *ext, _z_zbuf_t *zbf) { return _z_msg_ext_decode_unit(ext, zbf); } z_result_t _z_msg_ext_encode_zint(_z_wbuf_t *wbf, const _z_msg_ext_zint_t *ext) { z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, ext->_val)) return ret; } z_result_t _z_msg_ext_decode_zint(_z_msg_ext_zint_t *ext, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; ret |= _z_zint64_decode(&ext->_val, zbf); return ret; } z_result_t _z_msg_ext_decode_zint_na(_z_msg_ext_zint_t *ext, _z_zbuf_t *zbf) { return _z_msg_ext_decode_zint(ext, zbf); } z_result_t _z_msg_ext_encode_zbuf(_z_wbuf_t *wbf, const _z_msg_ext_zbuf_t *ext) { z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &ext->_val)) return ret; } z_result_t _z_msg_ext_decode_zbuf(_z_msg_ext_zbuf_t *ext, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; ret |= _z_slice_decode(&ext->_val, zbf); return ret; } z_result_t _z_msg_ext_decode_zbuf_na(_z_msg_ext_zbuf_t *ext, _z_zbuf_t *zbf) { return _z_msg_ext_decode_zbuf(ext, zbf); } /*------------------ Message Extension ------------------*/ z_result_t _z_msg_ext_encode(_z_wbuf_t *wbf, const _z_msg_ext_t *ext, bool has_next) { z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, (uint8_t)(_Z_EXT_FULL_ID(ext->_header) | (has_next << 7)))) uint8_t enc = _Z_EXT_ENC(ext->_header); switch (enc) { case _Z_MSG_EXT_ENC_UNIT: { _z_msg_ext_encode_unit(wbf, &ext->_body._unit); } break; case _Z_MSG_EXT_ENC_ZINT: { _z_msg_ext_encode_zint(wbf, &ext->_body._zint); } break; case _Z_MSG_EXT_ENC_ZBUF: { _z_msg_ext_encode_zbuf(wbf, &ext->_body._zbuf); } break; default: { _Z_INFO("WARNING: Trying to copy message extension with unknown encoding(%d)", enc); } break; } return ret; } z_result_t _z_msg_ext_unknown_body_decode(_z_msg_ext_body_t *body, uint8_t enc, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; switch (enc) { case _Z_MSG_EXT_ENC_UNIT: { ret |= _z_msg_ext_decode_unit(&body->_unit, zbf); } break; case _Z_MSG_EXT_ENC_ZINT: { ret |= _z_msg_ext_decode_zint(&body->_zint, zbf); } break; case _Z_MSG_EXT_ENC_ZBUF: { ret |= _z_msg_ext_decode_zbuf(&body->_zbuf, zbf); } break; default: { _Z_INFO("WARNING: Trying to copy message extension with unknown encoding(%d)", enc); } break; } return ret; } z_result_t _z_msg_ext_decode(_z_msg_ext_t *ext, _z_zbuf_t *zbf, bool *has_next) { z_result_t ret = _Z_RES_OK; ret |= _z_uint8_decode(&ext->_header, zbf); // Decode the header if (ret == _Z_RES_OK) { // TODO: define behaviour on decode failure, regarding zbuf allocation ret |= _z_msg_ext_unknown_body_decode(&ext->_body, _Z_EXT_ENC(ext->_header), zbf); } *has_next = (ext->_header & _Z_MSG_EXT_FLAG_Z) != 0; ext->_header &= _Z_EXT_FULL_ID_MASK; return ret; } z_result_t _z_msg_ext_decode_na(_z_msg_ext_t *ext, _z_zbuf_t *zbf, bool *has_next) { return _z_msg_ext_decode(ext, zbf, has_next); } z_result_t _z_msg_ext_vec_encode(_z_wbuf_t *wbf, const _z_msg_ext_vec_t *extensions) { z_result_t ret = _Z_RES_OK; size_t len = _z_msg_ext_vec_len(extensions); if (len > 0) { size_t i; for (i = 0; ret == _Z_RES_OK && i < len - 1; i++) { ret |= _z_msg_ext_encode(wbf, _z_msg_ext_vec_get(extensions, i), true); } if (ret == _Z_RES_OK) { ret |= _z_msg_ext_encode(wbf, _z_msg_ext_vec_get(extensions, i), true); } } return ret; } z_result_t _z_msg_ext_vec_push_callback(_z_msg_ext_t *extension, _z_msg_ext_vec_t *extensions) { _z_msg_ext_t *ext = (_z_msg_ext_t *)z_malloc(sizeof(_z_msg_ext_t)); if (ext == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *ext = *extension; *extension = _z_msg_ext_make_unit(0); _z_msg_ext_vec_append(extensions, extension); return 0; } z_result_t _z_msg_ext_vec_decode(_z_msg_ext_vec_t *extensions, _z_zbuf_t *zbf) { _z_msg_ext_vec_reset(extensions); return _z_msg_ext_decode_iter(zbf, (z_result_t(*)(_z_msg_ext_t *, void *))_z_msg_ext_vec_push_callback, (void *)extensions); } z_result_t _z_msg_ext_unknown_error(_z_msg_ext_t *extension, uint8_t trace_id) { #ifdef ZENOH_LOG_ERROR uint8_t ext_id = _Z_EXT_ID(extension->_header); switch (_Z_EXT_ENC(extension->_header)) { case _Z_MSG_EXT_ENC_UNIT: { _Z_ERROR("Unknown mandatory extension found (extension_id: %02x, trace_id: %02x), UNIT", ext_id, trace_id); break; } case _Z_MSG_EXT_ENC_ZINT: { _Z_ERROR("Unknown mandatory extension found (extension_id: %02x, trace_id: %02x), ZINT(%02jx)", ext_id, trace_id, (uintmax_t)extension->_body._zint._val); break; } case _Z_MSG_EXT_ENC_ZBUF: { _z_slice_t buf = extension->_body._zbuf._val; char *hex = (char *)z_malloc(buf.len * 2 + 1); if (hex == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } for (size_t i = 0; i < buf.len; ++i) { snprintf(hex + 2 * i, 3, "%02x", buf.start[i]); } _Z_ERROR("Unknown mandatory extension found (extension_id: %02x, trace_id: %02x), ZBUF(%.*s)", ext_id, trace_id, (int)buf.len * 2, hex); z_free(hex); break; } default: { _Z_ERROR("Unknown mandatory extension found (extension_id: %02x, trace_id: %02x), UNKOWN_ENCODING", ext_id, trace_id); } } #else _ZP_UNUSED(extension); _ZP_UNUSED(trace_id); #endif _Z_ERROR_RETURN(_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN); } z_result_t _z_msg_ext_skip_non_mandatory(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; if ((extension->_header & _Z_MSG_EXT_FLAG_M) != 0) { uint8_t trace_id = *(uint8_t *)ctx; ret = _z_msg_ext_unknown_error(extension, trace_id); } return ret; } z_result_t _z_msg_ext_decode_iter(_z_zbuf_t *zbf, z_result_t (*callback)(_z_msg_ext_t *, void *), void *context) { z_result_t ret = _Z_RES_OK; bool has_next = true; while (has_next && ret == _Z_RES_OK) { _z_msg_ext_t ext = _z_msg_ext_make_unit(0); ret |= _z_msg_ext_decode(&ext, zbf, &has_next); if (ret == _Z_RES_OK) { ret |= callback(&ext, context); _z_msg_ext_clear(&ext); } } return ret; } z_result_t _z_msg_ext_skip_non_mandatories(_z_zbuf_t *zbf, uint8_t trace_id) { return _z_msg_ext_decode_iter(zbf, _z_msg_ext_skip_non_mandatory, &trace_id); } ================================================ FILE: src/protocol/codec/interest.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/interest.h" #include #include #include #include #include "zenoh-pico/protocol/codec.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/core.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/system/platform.h" #define _Z_INTEREST_CODEC_FLAG_N 0x20 #define _Z_INTEREST_CODEC_FLAG_M 0x40 #define _Z_INTEREST_FLAG_COPY_MASK 0x9f // N & M flags occupy the place of C & F #define _Z_INTEREST_TRACE_ID 0x13 z_result_t _z_interest_encode(_z_wbuf_t *wbf, const _z_interest_t *interest, bool is_final) { // Set id _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, interest->_id)); if (is_final) { return _Z_RES_OK; } // Copy flags but clear current and future that are already processed. uint8_t flags = interest->flags; _Z_CLEAR_FLAG(flags, _Z_INTEREST_FLAG_CURRENT); _Z_CLEAR_FLAG(flags, _Z_INTEREST_FLAG_FUTURE); // Process restricted flag if (_Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_RESTRICTED)) { // Set Named & Mapping flags bool has_kesuffix = _z_wireexpr_has_suffix(&interest->_keyexpr); if (has_kesuffix) { _Z_SET_FLAG(flags, _Z_INTEREST_CODEC_FLAG_N); } if (_z_wireexpr_is_local(&interest->_keyexpr)) { _Z_SET_FLAG(flags, _Z_INTEREST_CODEC_FLAG_M); } // Set decl flags & keyexpr _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, flags)); _Z_RETURN_IF_ERR(_z_wireexpr_encode(wbf, has_kesuffix, &interest->_keyexpr)); } else { // Set decl flags _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, flags)); } return _Z_RES_OK; } z_result_t _z_interest_decode(_z_interest_t *interest, _z_zbuf_t *zbf, bool is_final, bool has_ext, uintptr_t mapping) { // Decode id _Z_RETURN_IF_ERR(_z_zint32_decode(&interest->_id, zbf)); if (!is_final) { uint8_t flags = 0; // Decode interest flags _Z_RETURN_IF_ERR(_z_uint8_decode(&flags, zbf)); // Process restricted flag if (_Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_RESTRICTED)) { // Decode ke _Z_RETURN_IF_ERR(_z_wireexpr_decode(&interest->_keyexpr, zbf, _Z_HAS_FLAG(flags, _Z_INTEREST_CODEC_FLAG_N), _Z_HAS_FLAG(flags, _Z_INTEREST_CODEC_FLAG_M), mapping)); } // Store interest flags (current and future already processed) interest->flags = (uint8_t)(interest->flags | (flags & _Z_INTEREST_FLAG_COPY_MASK)); } if (has_ext) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, _Z_INTEREST_TRACE_ID)); } return _Z_RES_OK; } ================================================ FILE: src/protocol/codec/message.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/message.h" #include #include #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/protocol/codec.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/core.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/result.h" /*=============================*/ /* Fields */ /*=============================*/ /*------------------ Payload field ------------------*/ z_result_t _z_payload_encode(_z_wbuf_t *wbf, const _z_slice_t *pld) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _PAYLOAD"); ret |= _z_slice_encode(wbf, pld); return ret; } z_result_t _z_payload_decode_na(_z_slice_t *pld, _z_zbuf_t *zbf) { _Z_DEBUG("Decoding _PAYLOAD"); return _z_slice_decode(pld, zbf); } z_result_t _z_payload_decode(_z_slice_t *pld, _z_zbuf_t *zbf) { return _z_payload_decode_na(pld, zbf); } z_result_t _z_id_encode_as_slice(_z_wbuf_t *wbf, const _z_id_t *id) { z_result_t ret = _Z_RES_OK; uint8_t len = _z_id_len(*id); if (len != 0) { _z_slice_t buf = _z_slice_alias_buf(id->id, len); ret = _z_slice_encode(wbf, &buf); } else { _Z_DEBUG("Attempted to encode invalid ID 0"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_ZENOH_UNKNOWN); ret = _Z_ERR_MESSAGE_ZENOH_UNKNOWN; } return ret; } /// Decodes a `zid` from the zbf, returning a negative value in case of error. /// /// Note that while `_z_id_t` has an error state (full 0s), this function doesn't /// guarantee that this state will be set in case of errors. z_result_t _z_id_decode_as_slice(_z_id_t *id, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; uint8_t len = _z_zbuf_read(zbf); _z_zbuf_read_bytes(zbf, id->id, 0, len); memset(id->id + len, 0, 16 - len); return ret; } /*------------------ Timestamp Field ------------------*/ z_result_t _z_timestamp_encode(_z_wbuf_t *wbf, const _z_timestamp_t *ts) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _TIMESTAMP"); _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, ts->time)) ret |= _z_id_encode_as_slice(wbf, &ts->id); return ret; } z_result_t _z_timestamp_encode_ext(_z_wbuf_t *wbf, const _z_timestamp_t *ts) { // Encode extension size then timestamp size_t ext_size = (size_t)(_z_zint_len(ts->time) + 1 + _z_id_len(ts->id)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, ext_size)); return _z_timestamp_encode(wbf, ts); } z_result_t _z_timestamp_decode(_z_timestamp_t *ts, _z_zbuf_t *zbf) { _Z_DEBUG("Decoding _TIMESTAMP"); z_result_t ret = _Z_RES_OK; ret |= _z_zint64_decode(&ts->time, zbf); ret |= _z_id_decode_as_slice(&ts->id, zbf); if (ret == _Z_RES_OK) { ts->valid = true; } return ret; } /*------------------ ResKey Field ------------------*/ z_result_t _z_wireexpr_encode(_z_wbuf_t *wbf, bool has_suffix, const _z_wireexpr_t *fld) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _RESKEY"); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, fld->_id)) if (has_suffix == true) { _Z_RETURN_IF_ERR(_z_string_encode(wbf, &fld->_suffix)) } return ret; } z_result_t _z_wireexpr_decode(_z_wireexpr_t *ke, _z_zbuf_t *zbf, bool has_suffix, bool remote_mapping, uintptr_t mapping) { _Z_DEBUG("Decoding _RESKEY"); z_result_t ret = _Z_RES_OK; ret |= _z_zint16_decode(&ke->_id, zbf); ke->_mapping = (remote_mapping) ? mapping : _Z_KEYEXPR_MAPPING_LOCAL; if (has_suffix) { _z_string_t str = _z_string_null(); ret |= _z_string_decode(&str, zbf); if (ret == _Z_RES_OK) { ke->_suffix = str; } else { ke->_suffix = _z_string_null(); } } else { ke->_suffix = _z_string_null(); } return ret; } /*------------------ Locators Field ------------------*/ z_result_t _z_locators_encode(_z_wbuf_t *wbf, const _z_locator_array_t *la) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _LOCATORS"); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, la->_len)) for (size_t i = 0; i < la->_len; i++) { _z_string_t s = _z_locator_to_string(&la->_val[i]); _Z_RETURN_IF_ERR(_z_string_encode(wbf, &s)) _z_string_clear(&s); } return ret; } z_result_t _z_locators_decode_na(_z_locator_array_t *a_loc, _z_zbuf_t *zbf) { _Z_DEBUG("Decoding _LOCATORS"); z_result_t ret = _Z_RES_OK; _z_zint_t len = 0; // Number of elements in the array ret |= _z_zsize_decode(&len, zbf); if (ret == _Z_RES_OK) { *a_loc = _z_locator_array_make(len); // Decode the elements for (size_t i = 0; i < len; i++) { _z_string_t str = _z_string_null(); ret |= _z_string_decode(&str, zbf); if (ret == _Z_RES_OK) { _z_locator_init(&a_loc->_val[i]); ret |= _z_locator_from_string(&a_loc->_val[i], &str); _z_string_clear(&str); } else { a_loc->_len = i; } } } else { *a_loc = _z_locator_array_make(0); } return ret; } z_result_t _z_locators_decode(_z_locator_array_t *a_loc, _z_zbuf_t *zbf) { return _z_locators_decode_na(a_loc, zbf); } /*=============================*/ /* Zenoh Messages */ /*=============================*/ z_result_t _z_source_info_decode(_z_source_info_t *info, _z_zbuf_t *zbf) { uint8_t zidlen = 0; _z_zint_t intbuf; z_result_t ret = _z_uint8_decode(&zidlen, zbf); if (ret == _Z_RES_OK) { zidlen = (zidlen >> 4) + 1; if (_z_zbuf_len(zbf) >= zidlen) { memset(info->_source_id.zid.id, 0, sizeof(info->_source_id.zid.id) / sizeof(info->_source_id.zid.id[0])); _z_zbuf_read_bytes(zbf, info->_source_id.zid.id, 0, zidlen); } else { _Z_INFO("Not enough bytes to read"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } if (ret == _Z_RES_OK) { ret = _z_zsize_decode(&intbuf, zbf); if (intbuf <= UINT32_MAX) { info->_source_id.eid = (uint32_t)intbuf; } else { _Z_INFO("Invalid value decoded"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } if (ret == _Z_RES_OK) { ret = _z_zsize_decode(&intbuf, zbf); if (intbuf <= UINT32_MAX) { info->_source_sn = (uint32_t)intbuf; } else { _Z_INFO("Invalid value decoded"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } return ret; } z_result_t _z_source_info_encode(_z_wbuf_t *wbf, const _z_source_info_t *info) { z_result_t ret = 0; uint8_t zidlen = _z_id_len(info->_source_id.zid); ret |= _z_uint8_encode(wbf, (uint8_t)((zidlen - 1) << 4)); _z_slice_t zid = _z_slice_alias_buf(info->_source_id.zid.id, zidlen); ret |= _z_slice_val_encode(wbf, &zid); ret |= _z_zsize_encode(wbf, info->_source_id.eid); ret |= _z_zsize_encode(wbf, info->_source_sn); return ret; } z_result_t _z_source_info_encode_ext(_z_wbuf_t *wbf, const _z_source_info_t *info) { z_result_t ret = 0; uint8_t zidlen = _z_id_len(info->_source_id.zid); size_t ext_size = 1u + zidlen + _z_zint_len(info->_source_id.eid) + _z_zint_len(info->_source_sn); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, ext_size)); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, (uint8_t)((zidlen - 1) << 4))); _z_slice_t zid = _z_slice_alias_buf(info->_source_id.zid.id, zidlen); _Z_RETURN_IF_ERR(_z_slice_val_encode(wbf, &zid)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, info->_source_id.eid)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, info->_source_sn)); return ret; } /*------------------ Push Body Field ------------------*/ z_result_t _z_push_body_encode(_z_wbuf_t *wbf, const _z_push_body_t *pshb) { uint8_t header = pshb->_is_put ? _Z_MID_Z_PUT : _Z_MID_Z_DEL; bool has_source_info = _z_id_check(pshb->_body._put._commons._source_info._source_id.zid) || pshb->_body._put._commons._source_info._source_sn != 0 || pshb->_body._put._commons._source_info._source_id.eid != 0; bool has_attachment = pshb->_is_put && _z_bytes_check(&pshb->_body._put._attachment); bool has_timestamp = _z_timestamp_check(&pshb->_body._put._commons._timestamp); bool has_encoding = false; if (has_source_info || has_attachment) { header |= _Z_FLAG_Z_Z; } if (pshb->_is_put) { if (has_timestamp) { header |= _Z_FLAG_Z_P_T; } has_encoding = _z_encoding_check(&pshb->_body._put._encoding); if (has_encoding) { header |= _Z_FLAG_Z_P_E; } } else { if (has_timestamp) { header |= _Z_FLAG_Z_D_T; } } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); if (has_timestamp) { _Z_RETURN_IF_ERR(_z_timestamp_encode(wbf, &pshb->_body._put._commons._timestamp)); } if (has_encoding) { _Z_RETURN_IF_ERR(_z_encoding_encode(wbf, &pshb->_body._put._encoding)); } if (has_source_info) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ENC_ZBUF | 0x01 | (has_attachment ? _Z_FLAG_Z_Z : 0))); _Z_RETURN_IF_ERR(_z_source_info_encode_ext(wbf, &pshb->_body._put._commons._source_info)); } if (has_attachment) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ENC_ZBUF | 0x03)); _Z_RETURN_IF_ERR(_z_bytes_encode(wbf, &pshb->_body._put._attachment)); } if (pshb->_is_put) { _Z_RETURN_IF_ERR(_z_bytes_encode(wbf, &pshb->_body._put._payload)); } return _Z_RES_OK; } z_result_t _z_push_body_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_push_body_t *pshb = (_z_push_body_t *)ctx; z_result_t ret = _Z_RES_OK; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZBUF | 0x01: { _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_source_info_decode(&pshb->_body._put._commons._source_info, &zbf); break; } case _Z_MSG_EXT_ENC_ZBUF | 0x03: { // Attachment _z_slice_t s; if (_z_slice_is_alloced(&extension->_body._zbuf._val)) { s = _z_slice_steal(&extension->_body._zbuf._val); } else { _Z_RETURN_IF_ERR(_z_slice_copy(&s, &extension->_body._zbuf._val)); } ret = _z_bytes_from_slice(&pshb->_body._put._attachment, &s); break; } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { ret = _z_msg_ext_unknown_error(extension, 0x08); } } return ret; } z_result_t _z_push_body_decode(_z_push_body_t *pshb, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs) { z_result_t ret = _Z_RES_OK; switch (_Z_MID(header)) { case _Z_MID_Z_PUT: { pshb->_is_put = true; if (_Z_HAS_FLAG(header, _Z_FLAG_Z_P_T)) { _Z_RETURN_IF_ERR(_z_timestamp_decode(&pshb->_body._put._commons._timestamp, zbf)); } if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_Z_P_E)) { _Z_RETURN_IF_ERR(_z_encoding_decode(&pshb->_body._put._encoding, zbf)); } if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_push_body_decode_extensions, pshb)); } if (ret == _Z_RES_OK) { _Z_RETURN_IF_ERR(_z_bytes_decode(&pshb->_body._put._payload, zbf, arcs)); } break; } case _Z_MID_Z_DEL: { pshb->_is_put = false; if (_Z_HAS_FLAG(header, _Z_FLAG_Z_D_T)) { _Z_RETURN_IF_ERR(_z_timestamp_decode(&pshb->_body._put._commons._timestamp, zbf)); } if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_push_body_decode_extensions, pshb)); } break; } default: { _Z_ERROR_LOG(_Z_ERR_MESSAGE_ZENOH_UNKNOWN); ret = _Z_ERR_MESSAGE_ZENOH_UNKNOWN; } } return ret; } z_result_t _z_put_encode(_z_wbuf_t *wbf, const _z_msg_put_t *put) { _z_push_body_t body = {._is_put = true, ._body = {._put = *put}}; return _z_push_body_encode(wbf, &body); } z_result_t _z_put_decode(_z_msg_put_t *put, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs) { assert(_Z_MID(header) == _Z_MID_Z_PUT); _z_push_body_t body = {._is_put = true, ._body = {._put = *put}}; z_result_t ret = _z_push_body_decode(&body, zbf, header, arcs); *put = body._body._put; return ret; } z_result_t _z_del_encode(_z_wbuf_t *wbf, const _z_msg_del_t *del) { _z_push_body_t body = {._is_put = false, ._body = {._del = *del}}; return _z_push_body_encode(wbf, &body); } z_result_t _z_del_decode(_z_msg_del_t *del, _z_zbuf_t *zbf, uint8_t header) { assert(_Z_MID(header) == _Z_MID_Z_DEL); _z_push_body_t body = {._is_put = false, ._body = {._del = *del}}; z_result_t ret = _z_push_body_decode(&body, zbf, header, NULL); *del = body._body._del; return ret; } /*------------------ Query Message ------------------*/ z_result_t _z_query_encode(_z_wbuf_t *wbf, const _z_msg_query_t *msg) { z_result_t ret = _Z_RES_OK; uint8_t header = _Z_MID_Z_QUERY; bool has_params = _z_slice_check(&msg->_parameters) && msg->_parameters.len > 0; if (has_params || msg->_implicit_anyke) { _Z_SET_FLAG(header, _Z_FLAG_Z_Q_P); } bool has_consolidation = (msg->_consolidation != Z_CONSOLIDATION_MODE_DEFAULT); if (has_consolidation) { _Z_SET_FLAG(header, _Z_FLAG_Z_Q_C); } _z_msg_query_reqexts_t required_exts = _z_msg_query_required_extensions(msg); if (required_exts.body || required_exts.info || required_exts.attachment) { header |= _Z_FLAG_Z_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); if (has_consolidation) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, msg->_consolidation)); } if (msg->_implicit_anyke) { if (has_params) { _z_slice_t anykey_slice = _z_slice_from_buf_custom_deleter( (uint8_t *)_Z_QUERY_PARAMS_LIST_SEPARATOR _Z_QUERY_PARAMS_KEY_ANYKE, _Z_QUERY_PARAMS_LIST_SEPARATOR_LEN + _Z_QUERY_PARAMS_KEY_ANYKE_LEN, _z_delete_context_static()); _z_slice_t combined[2] = {msg->_parameters, anykey_slice}; _Z_RETURN_IF_ERR(_z_slices_encode(wbf, combined, 2)); } else { _z_slice_t anykey_slice = _z_slice_from_buf_custom_deleter( (uint8_t *)_Z_QUERY_PARAMS_KEY_ANYKE, _Z_QUERY_PARAMS_KEY_ANYKE_LEN, _z_delete_context_static()); _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &anykey_slice)); } } else if (has_params) { _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &msg->_parameters)); } if (required_exts.body) { uint8_t extheader = _Z_MSG_EXT_ENC_ZBUF | 0x03; if (required_exts.info || required_exts.attachment) { extheader |= _Z_FLAG_Z_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_value_encode(wbf, &msg->_ext_value)); } if (required_exts.info) { uint8_t extheader = _Z_MSG_EXT_ENC_ZBUF | 0x01; if (required_exts.attachment) { extheader |= _Z_FLAG_Z_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_source_info_encode_ext(wbf, &msg->_ext_info)); } if (required_exts.attachment) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ENC_ZBUF | 0x05)); _Z_RETURN_IF_ERR(_z_bytes_encode(wbf, &msg->_ext_attachment)); } return ret; } z_result_t _z_query_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_msg_query_t *msg = (_z_msg_query_t *)ctx; z_result_t ret = _Z_RES_OK; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZBUF | 0x01: { // Source Info _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_source_info_decode(&msg->_ext_info, &zbf); break; } case _Z_MSG_EXT_ENC_ZBUF | 0x03: { // Payload _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_value_decode(&msg->_ext_value, &zbf); break; } case _Z_MSG_EXT_ENC_ZBUF | 0x05: { // Attachment _z_slice_t s; if (_z_slice_is_alloced(&extension->_body._zbuf._val)) { s = _z_slice_steal(&extension->_body._zbuf._val); } else { _Z_RETURN_IF_ERR(_z_slice_copy(&s, &extension->_body._zbuf._val)); } ret = _z_bytes_from_slice(&msg->_ext_attachment, &s); break; } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { ret = _z_msg_ext_unknown_error(extension, 0x09); } } return ret; } z_result_t _z_query_decode(_z_msg_query_t *msg, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_Z_QUERY"); z_result_t ret = _Z_RES_OK; msg->_implicit_anyke = false; // implicit_anyke is always false on reception, since the presence of the _anyke parameter is signaled by // the presence of the _anyke key in the parameters list, which is parsed later. if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Q_C)) { _Z_RETURN_IF_ERR(_z_uint8_decode((uint8_t *)&msg->_consolidation, zbf)); } else { msg->_consolidation = Z_CONSOLIDATION_MODE_DEFAULT; } if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Q_P)) { _Z_RETURN_IF_ERR(_z_slice_decode(&msg->_parameters, zbf)); } else { _z_slice_clear(&msg->_parameters); } if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_query_decode_extensions, msg)); } return ret; } z_result_t _z_reply_encode(_z_wbuf_t *wbf, const _z_msg_reply_t *reply) { uint8_t header = _Z_MID_Z_REPLY; bool has_consolidation = reply->_consolidation != Z_CONSOLIDATION_MODE_DEFAULT; if (has_consolidation) { _Z_SET_FLAG(header, _Z_FLAG_Z_R_C); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); if (has_consolidation) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, reply->_consolidation)); } _Z_RETURN_IF_ERR(_z_push_body_encode(wbf, &reply->_body)); return _Z_RES_OK; } z_result_t _z_reply_decode_extension(_z_msg_ext_t *extension, void *ctx) { _ZP_UNUSED(ctx); z_result_t ret = _Z_RES_OK; switch (_Z_EXT_FULL_ID(extension->_header)) { default: ret = _z_msg_ext_unknown_error(extension, 0x0a); break; } return ret; } z_result_t _z_reply_decode(_z_msg_reply_t *reply, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs) { if (_Z_HAS_FLAG(header, _Z_FLAG_Z_R_C)) { _Z_RETURN_IF_ERR(_z_uint8_decode((uint8_t *)&reply->_consolidation, zbf)); } else { reply->_consolidation = Z_CONSOLIDATION_MODE_DEFAULT; } if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_reply_decode_extension, reply)); } uint8_t put_header = 0; _Z_RETURN_IF_ERR(_z_uint8_decode(&put_header, zbf)); _Z_RETURN_IF_ERR(_z_push_body_decode(&reply->_body, zbf, put_header, arcs)); return _Z_RES_OK; } z_result_t _z_err_encode(_z_wbuf_t *wbf, const _z_msg_err_t *err) { z_result_t ret = _Z_RES_OK; uint8_t header = _Z_MID_Z_ERR; // Encode header bool has_encoding = _z_encoding_check(&err->_encoding); if (has_encoding) { _Z_SET_FLAG(header, _Z_FLAG_Z_E_E); } bool has_sinfo_ext = _z_id_check(err->_ext_source_info._source_id.zid) || err->_ext_source_info._source_id.eid != 0 || err->_ext_source_info._source_sn != 0; if (has_sinfo_ext) { _Z_SET_FLAG(header, _Z_FLAG_Z_Z); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); // Encode encoding if (has_encoding) { _Z_RETURN_IF_ERR(_z_encoding_encode(wbf, &err->_encoding)); } // Encode extensions if (has_sinfo_ext) { uint8_t extheader = _Z_MSG_EXT_ENC_ZBUF | 0x01; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_source_info_encode_ext(wbf, &err->_ext_source_info)); } // Encode payload _Z_RETURN_IF_ERR(_z_bytes_encode(wbf, &err->_payload)); return ret; } z_result_t _z_err_decode_extension(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; _z_msg_err_t *reply = (_z_msg_err_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZBUF | 0x01: { _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_source_info_decode(&reply->_ext_source_info, &zbf); break; } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { ret = _z_msg_ext_unknown_error(extension, 0x0a); } } return ret; } z_result_t _z_err_decode(_z_msg_err_t *err, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs) { if (_Z_HAS_FLAG(header, _Z_FLAG_Z_E_E)) { _Z_RETURN_IF_ERR(_z_encoding_decode(&err->_encoding, zbf)); } if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_err_decode_extension, err)); } _Z_RETURN_IF_ERR(_z_bytes_decode(&err->_payload, zbf, arcs)); return _Z_RES_OK; } /*=============================*/ /* Scouting Messages */ /*=============================*/ /*------------------ Scout Message ------------------*/ z_result_t _z_scout_encode(_z_wbuf_t *wbf, uint8_t header, const _z_s_msg_scout_t *msg) { z_result_t ret = _Z_RES_OK; (void)(header); _Z_DEBUG("Encoding _Z_MID_SCOUT"); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, msg->_version)) uint8_t cbyte = msg->_what & 0x07; uint8_t zid_len = _z_id_len(msg->_zid); if (zid_len > 0) { _Z_SET_FLAG(cbyte, _Z_FLAG_T_SCOUT_I); cbyte |= (uint8_t)(((zid_len - 1) & 0x0F) << 4); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)) ret |= _z_wbuf_write_bytes(wbf, msg->_zid.id, 0, zid_len); return ret; } z_result_t _z_scout_decode(_z_s_msg_scout_t *msg, _z_zbuf_t *zbf, uint8_t header) { z_result_t ret = _Z_RES_OK; (void)(header); _Z_DEBUG("Decoding _Z_MID_SCOUT"); *msg = (_z_s_msg_scout_t){0}; ret |= _z_uint8_decode(&msg->_version, zbf); uint8_t cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_what = cbyte & 0x07; msg->_zid = _z_id_empty(); if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(cbyte, _Z_FLAG_T_SCOUT_I) == true)) { uint8_t zidlen = ((cbyte & 0xF0) >> 4) + (uint8_t)1; _z_zbuf_read_bytes(zbf, msg->_zid.id, 0, zidlen); } return ret; } /*------------------ Hello Message ------------------*/ z_result_t _z_hello_encode(_z_wbuf_t *wbf, uint8_t header, const _z_s_msg_hello_t *msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_HELLO"); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, msg->_version)) uint8_t zidlen = _z_id_len(msg->_zid); uint8_t cbyte = 0; cbyte |= _z_whatami_to_uint8(msg->_whatami); cbyte |= (uint8_t)(((zidlen - 1) & 0x0F) << 4); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)) _z_slice_t s = _z_slice_alias_buf(msg->_zid.id, zidlen); _Z_RETURN_IF_ERR(_z_slice_val_encode(wbf, &s)); if (_Z_HAS_FLAG(header, _Z_FLAG_T_HELLO_L) == true) { _Z_RETURN_IF_ERR(_z_locators_encode(wbf, &msg->_locators)) } return ret; } z_result_t _z_hello_decode_na(_z_s_msg_hello_t *msg, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_HELLO"); z_result_t ret = _Z_RES_OK; *msg = (_z_s_msg_hello_t){0}; ret |= _z_uint8_decode(&msg->_version, zbf); uint8_t cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_whatami = _z_whatami_from_uint8(cbyte); uint8_t zidlen = ((cbyte & 0xF0) >> 4) + (uint8_t)1; if (ret == _Z_RES_OK) { msg->_zid = _z_id_empty(); _z_zbuf_read_bytes(zbf, msg->_zid.id, 0, zidlen); } else { msg->_zid = _z_id_empty(); } if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_HELLO_L) == true)) { ret |= _z_locators_decode(&msg->_locators, zbf); if (ret != _Z_RES_OK) { msg->_locators = _z_locator_array_empty(); } } else { msg->_locators = _z_locator_array_empty(); } return ret; } z_result_t _z_hello_decode(_z_s_msg_hello_t *msg, _z_zbuf_t *zbf, uint8_t header) { return _z_hello_decode_na(msg, zbf, header); } z_result_t _z_scouting_message_encode(_z_wbuf_t *wbf, const _z_scouting_message_t *msg) { z_result_t ret = _Z_RES_OK; uint8_t header = msg->_header; _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, header)) switch (_Z_MID(msg->_header)) { case _Z_MID_SCOUT: { ret |= _z_scout_encode(wbf, msg->_header, &msg->_body._scout); } break; case _Z_MID_HELLO: { ret |= _z_hello_encode(wbf, msg->_header, &msg->_body._hello); } break; default: { _Z_INFO("WARNING: Trying to encode session message with unknown ID(%d)", _Z_MID(msg->_header)); ret |= _Z_ERR_MESSAGE_TRANSPORT_UNKNOWN; } break; } return ret; } z_result_t _z_scouting_message_decode_na(_z_scouting_message_t *msg, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; *msg = (_z_scouting_message_t){0}; bool is_last = false; do { ret |= _z_uint8_decode(&msg->_header, zbf); // Decode the header if (ret == _Z_RES_OK) { uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_SCOUT: { ret |= _z_scout_decode(&msg->_body._scout, zbf, msg->_header); is_last = true; } break; case _Z_MID_HELLO: { ret |= _z_hello_decode(&msg->_body._hello, zbf, msg->_header); is_last = true; } break; default: { _Z_INFO("WARNING: Trying to decode scouting message with unknown ID(0x%x)", mid); ret |= _Z_ERR_MESSAGE_TRANSPORT_UNKNOWN; is_last = true; } break; } } else { msg->_header = 0xFF; } } while ((ret == _Z_RES_OK) && (is_last == false)); if ((ret == _Z_RES_OK) && (msg->_header & _Z_MSG_EXT_FLAG_Z) != 0) { ret |= _z_msg_ext_skip_non_mandatories(zbf, 0x06); } return ret; } z_result_t _z_scouting_message_decode(_z_scouting_message_t *s_msg, _z_zbuf_t *zbf) { return _z_scouting_message_decode_na(s_msg, zbf); } ================================================ FILE: src/protocol/codec/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/network.h" #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/codec.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/declarations.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/codec/interest.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/core.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" /*------------------ Push Message ------------------*/ z_result_t _z_push_encode(_z_wbuf_t *wbf, const _z_n_msg_push_t *msg) { uint8_t header = _Z_MID_N_PUSH | (_z_wireexpr_is_local(&msg->_key) ? _Z_FLAG_N_REQUEST_M : 0); bool has_suffix = _z_wireexpr_has_suffix(&msg->_key); bool has_qos_ext = msg->_qos._val != _Z_N_QOS_DEFAULT._val; bool has_timestamp_ext = _z_timestamp_check(&msg->_timestamp); if (has_suffix) { header |= _Z_FLAG_N_REQUEST_N; } if (has_qos_ext || has_timestamp_ext) { header |= _Z_FLAG_N_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_wireexpr_encode(wbf, has_suffix, &msg->_key)); if (has_qos_ext) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, (uint8_t)(_Z_MSG_EXT_ENC_ZINT | 0x01 | (has_timestamp_ext << 7)))); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, msg->_qos._val)); } if (has_timestamp_ext) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ENC_ZBUF | 0x02)); _Z_RETURN_IF_ERR(_z_timestamp_encode_ext(wbf, &msg->_timestamp)); } _Z_RETURN_IF_ERR(_z_push_body_encode(wbf, &msg->_body)); return _Z_RES_OK; } z_result_t _z_push_decode_ext_cb(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; _z_n_msg_push_t *msg = (_z_n_msg_push_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZINT | 0x01: { // QOS ext if (extension->_body._zint._val > UINT32_MAX) { _Z_INFO("Invalid value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } msg->_qos = (_z_n_qos_t){._val = (uint8_t)extension->_body._zint._val}; break; } case _Z_MSG_EXT_ENC_ZBUF | 0x02: { // Timestamp ext _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_timestamp_decode(&msg->_timestamp, &zbf); break; } default: if ((extension->_header & _Z_MSG_EXT_FLAG_M) != 0) { ret = _z_msg_ext_unknown_error(extension, 0x07); } } return ret; } z_result_t _z_push_decode(_z_n_msg_push_t *msg, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping) { z_result_t ret = _Z_RES_OK; msg->_qos = _Z_N_QOS_DEFAULT; ret |= _z_wireexpr_decode(&msg->_key, zbf, _Z_HAS_FLAG(header, _Z_FLAG_N_PUSH_N), _Z_HAS_FLAG(header, _Z_FLAG_N_PUSH_M), mapping); if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_N_Z)) { ret = _z_msg_ext_decode_iter(zbf, _z_push_decode_ext_cb, msg); } if (ret == _Z_RES_OK) { uint8_t msgheader; _Z_RETURN_IF_ERR(_z_uint8_decode(&msgheader, zbf)); _Z_RETURN_IF_ERR(_z_push_body_decode(&msg->_body, zbf, msgheader, arcs)); } return ret; } /*------------------ Request Message ------------------*/ z_result_t _z_request_encode(_z_wbuf_t *wbf, const _z_n_msg_request_t *msg) { z_result_t ret = _Z_RES_OK; uint8_t header = _Z_MID_N_REQUEST | (_z_wireexpr_is_local(&msg->_key) ? _Z_FLAG_N_REQUEST_M : 0); bool has_suffix = _z_wireexpr_has_suffix(&msg->_key); if (has_suffix) { header |= _Z_FLAG_N_REQUEST_N; } _z_n_msg_request_exts_t exts = _z_n_msg_request_needed_exts(msg); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, (uint8_t)(header | (exts.n != 0 ? _Z_FLAG_Z_Z : 0)))); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_rid)); _Z_RETURN_IF_ERR(_z_wireexpr_encode(wbf, has_suffix, &msg->_key)); if (exts.ext_qos) { exts.n -= 1; uint8_t extheader = 0x01 | _Z_MSG_EXT_ENC_ZINT | (exts.n ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_ext_qos._val)); } if (exts.ext_tstamp) { exts.n -= 1; uint8_t extheader = 0x02 | _Z_MSG_EXT_ENC_ZBUF | (exts.n ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_timestamp_encode_ext(wbf, &msg->_ext_timestamp)); } if (exts.ext_target) { exts.n -= 1; uint8_t extheader = 0x04 | _Z_MSG_EXT_ENC_ZINT | (exts.n ? _Z_FLAG_Z_Z : 0) | _Z_MSG_EXT_FLAG_M; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_ext_target)); } if (exts.ext_budget) { exts.n -= 1; uint8_t extheader = 0x05 | _Z_MSG_EXT_ENC_ZINT | (exts.n ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_ext_budget)); } if (exts.ext_timeout_ms) { exts.n -= 1; uint8_t extheader = 0x06 | _Z_MSG_EXT_ENC_ZINT | (exts.n ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, msg->_ext_timeout_ms)); } switch (msg->_tag) { case _Z_REQUEST_QUERY: { _Z_RETURN_IF_ERR(_z_query_encode(wbf, &msg->_body._query)); } break; case _Z_REQUEST_PUT: { _Z_RETURN_IF_ERR(_z_put_encode(wbf, &msg->_body._put)); } break; case _Z_REQUEST_DEL: { _Z_RETURN_IF_ERR(_z_del_encode(wbf, &msg->_body._del)); } break; } return ret; } z_result_t _z_request_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_n_msg_request_t *msg = (_z_n_msg_request_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case 0x01 | _Z_MSG_EXT_ENC_ZINT: { // QOS ext if (extension->_body._zint._val > UINT8_MAX) { _Z_INFO("Invalid value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } msg->_ext_qos = (_z_n_qos_t){._val = (uint8_t)extension->_body._zint._val}; break; } case 0x02 | _Z_MSG_EXT_ENC_ZBUF: { // Timestamp ext _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); _Z_RETURN_IF_ERR(_z_timestamp_decode(&msg->_ext_timestamp, &zbf)); break; } case 0x04 | _Z_MSG_EXT_ENC_ZINT | _Z_MSG_EXT_FLAG_M: { msg->_ext_target = (uint8_t)extension->_body._zint._val; if (msg->_ext_target > 2) { _Z_INFO("Invalid value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } } break; case 0x05 | _Z_MSG_EXT_ENC_ZINT: { if (extension->_body._zint._val > UINT32_MAX) { _Z_INFO("Request extension budget was truncated to u32."); } msg->_ext_budget = (uint32_t)extension->_body._zint._val; } break; case 0x06 | _Z_MSG_EXT_ENC_ZINT: { msg->_ext_timeout_ms = extension->_body._zint._val; } break; default: if ((extension->_header & _Z_MSG_EXT_FLAG_M) != 0) { return _z_msg_ext_unknown_error(extension, 0x16); } } return _Z_RES_OK; } z_result_t _z_request_decode(_z_n_msg_request_t *msg, _z_zbuf_t *zbf, const uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping) { msg->_ext_qos = _Z_N_QOS_DEFAULT; _Z_RETURN_IF_ERR(_z_zsize_decode(&msg->_rid, zbf)); _Z_RETURN_IF_ERR(_z_wireexpr_decode(&msg->_key, zbf, _Z_HAS_FLAG(header, _Z_FLAG_N_REQUEST_N), _Z_HAS_FLAG(header, _Z_FLAG_N_REQUEST_M), mapping)); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_request_decode_extensions, msg)); } uint8_t zheader; _Z_RETURN_IF_ERR(_z_uint8_decode(&zheader, zbf)); switch (_Z_MID(zheader)) { case _Z_MID_Z_QUERY: { msg->_tag = _Z_REQUEST_QUERY; _Z_RETURN_IF_ERR(_z_query_decode(&msg->_body._query, zbf, zheader)); } break; case _Z_MID_Z_PUT: { msg->_tag = _Z_REQUEST_PUT; _Z_RETURN_IF_ERR(_z_put_decode(&msg->_body._put, zbf, zheader, arcs)); } break; case _Z_MID_Z_DEL: { msg->_tag = _Z_REQUEST_DEL; _Z_RETURN_IF_ERR(_z_del_decode(&msg->_body._del, zbf, zheader)); } break; default: _Z_INFO("Unknown request type received: %d", _Z_MID(zheader)); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } return _Z_RES_OK; } /*------------------ Response Message ------------------*/ z_result_t _z_response_encode(_z_wbuf_t *wbf, const _z_n_msg_response_t *msg) { z_result_t ret = _Z_RES_OK; uint8_t header = _Z_MID_N_RESPONSE; _Z_DEBUG("Encoding _Z_MID_N_RESPONSE"); bool has_qos_ext = msg->_ext_qos._val != _Z_N_QOS_DEFAULT._val; bool has_ts_ext = _z_timestamp_check(&msg->_ext_timestamp); bool has_responder_ext = _z_id_check(msg->_ext_responder._zid) || msg->_ext_responder._eid != 0; int n_ext = (has_qos_ext ? 1 : 0) + (has_ts_ext ? 1 : 0) + (has_responder_ext ? 1 : 0); bool has_suffix = _z_wireexpr_has_suffix(&msg->_key); if (_z_wireexpr_is_local(&msg->_key)) { _Z_SET_FLAG(header, _Z_FLAG_N_RESPONSE_M); } if (has_suffix) { _Z_SET_FLAG(header, _Z_FLAG_N_RESPONSE_N); } if (n_ext != 0) { _Z_SET_FLAG(header, _Z_FLAG_Z_Z); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_request_id)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_key._id)); if (has_suffix) { _Z_RETURN_IF_ERR(_z_string_encode(wbf, &msg->_key._suffix)) } if (has_qos_ext) { n_ext -= 1; uint8_t extheader = _Z_MSG_EXT_ENC_ZINT | 0x01; if (n_ext != 0) { extheader |= _Z_FLAG_Z_Z; } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_ext_qos._val)); } if (has_ts_ext) { n_ext -= 1; uint8_t extheader = _Z_MSG_EXT_ENC_ZBUF | 0x02 | (n_ext != 0 ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_timestamp_encode_ext(wbf, &msg->_ext_timestamp)); } if (has_responder_ext) { n_ext -= 1; uint8_t extheader = _Z_MSG_EXT_ENC_ZBUF | 0x03 | (n_ext != 0 ? _Z_FLAG_Z_Z : 0); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); uint8_t zidlen = _z_id_len(msg->_ext_responder._zid); extheader = (uint8_t)((zidlen - 1) << 4); size_t ext_size = (size_t)(zidlen + 1 + _z_zint_len(msg->_ext_responder._eid)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, ext_size)); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, extheader)); _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, msg->_ext_responder._zid.id, 0, zidlen)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_ext_responder._eid)); } switch (msg->_tag) { case _Z_RESPONSE_BODY_REPLY: { _Z_RETURN_IF_ERR(_z_reply_encode(wbf, &msg->_body._reply)); break; } case _Z_RESPONSE_BODY_ERR: { _Z_RETURN_IF_ERR(_z_err_encode(wbf, &msg->_body._err)); break; } } return ret; } z_result_t _z_response_decode_extension(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; _z_n_msg_response_t *msg = (_z_n_msg_response_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZINT | 0x01: { msg->_ext_qos._val = (uint8_t)extension->_body._zint._val; break; } case _Z_MSG_EXT_ENC_ZBUF | 0x02: { _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); ret = _z_timestamp_decode(&msg->_ext_timestamp, &zbf); break; } case _Z_MSG_EXT_ENC_ZBUF | 0x03: { _z_zbuf_t _zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); _z_zbuf_t *zbf = &_zbf; uint8_t header; _Z_RETURN_IF_ERR(_z_uint8_decode(&header, zbf)); uint8_t zidlen = (header >> 4) + (uint8_t)1; _Z_RETURN_IF_ERR(_z_zbuf_read_exact(zbf, msg->_ext_responder._zid.id, zidlen)); _Z_RETURN_IF_ERR(_z_zint32_decode(&msg->_ext_responder._eid, zbf)); break; } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { ret = _z_msg_ext_unknown_error(extension, 0x0d); } } return ret; } z_result_t _z_response_decode(_z_n_msg_response_t *msg, _z_zbuf_t *zbf, uint8_t header, _z_arc_slice_t *arcs, uintptr_t mapping) { _Z_DEBUG("Decoding _Z_MID_N_RESPONSE"); msg->_ext_qos = _Z_N_QOS_DEFAULT; _Z_RETURN_IF_ERR(_z_zsize_decode(&msg->_request_id, zbf)); _Z_RETURN_IF_ERR(_z_wireexpr_decode(&msg->_key, zbf, _Z_HAS_FLAG(header, _Z_FLAG_N_RESPONSE_N), _Z_HAS_FLAG(header, _Z_FLAG_N_RESPONSE_M), mapping)); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_response_decode_extension, msg)); } uint8_t inner_header; _Z_RETURN_IF_ERR(_z_uint8_decode(&inner_header, zbf)); switch (_Z_MID(inner_header)) { case _Z_MID_Z_REPLY: { msg->_tag = _Z_RESPONSE_BODY_REPLY; return _z_reply_decode(&msg->_body._reply, zbf, inner_header, arcs); break; } case _Z_MID_Z_ERR: { msg->_tag = _Z_RESPONSE_BODY_ERR; return _z_err_decode(&msg->_body._err, zbf, inner_header, arcs); break; } default: { _Z_ERROR("Unknown N_MID: %d", _Z_MID(inner_header)); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } } } /*------------------ Response Final Message ------------------*/ z_result_t _z_response_final_encode(_z_wbuf_t *wbf, const _z_n_msg_response_final_t *msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_N_RESPONSE_FINAL"); uint8_t header = _Z_MID_N_RESPONSE_FINAL; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_request_id)); return ret; } z_result_t _z_response_final_decode(_z_n_msg_response_final_t *msg, _z_zbuf_t *zbf, uint8_t header) { (void)(header); z_result_t ret = _Z_RES_OK; ret |= _z_zsize_decode(&msg->_request_id, zbf); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x1a)); } return ret; } z_result_t _z_declare_encode(_z_wbuf_t *wbf, const _z_n_msg_declare_t *decl) { uint8_t header = _Z_MID_N_DECLARE; bool has_qos_ext = decl->_ext_qos._val != _Z_N_QOS_DEFAULT._val; bool has_timestamp_ext = _z_timestamp_check(&decl->_ext_timestamp); int n_ext = (has_qos_ext ? 1 : 0) + (has_timestamp_ext ? 1 : 0); if (n_ext != 0) { header |= _Z_FLAG_N_Z; } if (decl->_interest_id.has_value) { header |= _Z_FLAG_N_DECLARE_I; } // Encode header _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); // Encode interest id if (decl->_interest_id.has_value) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, decl->_interest_id.value)); } // Encode extensions if (has_qos_ext) { n_ext -= 1; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, 0x01 | _Z_MSG_EXT_ENC_ZINT | (n_ext != 0 ? _Z_FLAG_Z_Z : 0))); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, decl->_ext_qos._val)); } if (has_timestamp_ext) { n_ext -= 1; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, 0x02 | _Z_MSG_EXT_ENC_ZBUF | (n_ext != 0 ? _Z_FLAG_Z_Z : 0))); _Z_RETURN_IF_ERR(_z_timestamp_encode_ext(wbf, &decl->_ext_timestamp)); } // Encode declaration return _z_declaration_encode(wbf, &decl->_decl); } z_result_t _z_declare_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_n_msg_declare_t *decl = (_z_n_msg_declare_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZINT | 0x01: { decl->_ext_qos._val = (uint8_t)extension->_body._zint._val; break; } case _Z_MSG_EXT_ENC_ZBUF | 0x02: { _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); return _z_timestamp_decode(&decl->_ext_timestamp, &zbf); } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { return _z_msg_ext_unknown_error(extension, 0x19); } } return _Z_RES_OK; } z_result_t _z_declare_decode(_z_n_msg_declare_t *decl, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { decl->_ext_qos = _Z_N_QOS_DEFAULT; // Retrieve interest id if (_Z_HAS_FLAG(header, _Z_FLAG_N_DECLARE_I)) { _Z_RETURN_IF_ERR(_z_zint32_decode(&decl->_interest_id.value, zbf)); decl->_interest_id.has_value = true; } // Decode extensions if (_Z_HAS_FLAG(header, _Z_FLAG_N_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_declare_decode_extensions, decl)) } // Decode declaration return _z_declaration_decode(&decl->_decl, zbf, mapping); } z_result_t _z_n_interest_encode(_z_wbuf_t *wbf, const _z_n_msg_interest_t *interest) { // Set header uint8_t header = _Z_MID_N_INTEREST; bool is_final = true; if (_Z_HAS_FLAG(interest->_interest.flags, _Z_INTEREST_FLAG_CURRENT)) { is_final = false; _Z_SET_FLAG(header, _Z_FLAG_N_INTEREST_CURRENT); } if (_Z_HAS_FLAG(interest->_interest.flags, _Z_INTEREST_FLAG_FUTURE)) { is_final = false; _Z_SET_FLAG(header, _Z_FLAG_N_INTEREST_FUTURE); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); return _z_interest_encode(wbf, &interest->_interest, is_final); } z_result_t _z_n_interest_decode(_z_n_msg_interest_t *interest, _z_zbuf_t *zbf, uint8_t header, uintptr_t mapping) { interest->_interest = _z_interest_null(); bool is_final = true; bool has_ext = false; if (_Z_HAS_FLAG(header, _Z_FLAG_N_INTEREST_CURRENT)) { _Z_SET_FLAG(interest->_interest.flags, _Z_INTEREST_FLAG_CURRENT); is_final = false; } if (_Z_HAS_FLAG(header, _Z_FLAG_N_INTEREST_FUTURE)) { _Z_SET_FLAG(interest->_interest.flags, _Z_INTEREST_FLAG_FUTURE); is_final = false; } // Decode extention if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { has_ext = true; } return _z_interest_decode(&interest->_interest, zbf, is_final, has_ext, mapping); } z_result_t _z_oam_encode(_z_wbuf_t *wbf, const _z_n_msg_oam_t *oam) { uint8_t header = _Z_MID_N_OAM; bool has_qos_ext = oam->_ext_qos._val != _Z_N_QOS_DEFAULT._val; bool has_timestamp_ext = _z_timestamp_check(&oam->_ext_timestamp); int n_ext = (has_qos_ext ? 1 : 0) + (has_timestamp_ext ? 1 : 0); if (n_ext != 0) { header |= _Z_FLAG_N_Z; } switch (oam->_enc) { case _Z_OAM_BODY_UNIT: { header |= _Z_MSG_EXT_ENC_UNIT; } break; case _Z_OAM_BODY_ZINT: { header |= _Z_MSG_EXT_ENC_ZINT; } break; case _Z_OAM_BODY_ZBUF: { header |= _Z_MSG_EXT_ENC_ZBUF; } break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, header)); _Z_RETURN_IF_ERR(_z_zint16_encode(wbf, oam->_id)); if (has_qos_ext) { n_ext -= 1; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, 0x01 | _Z_MSG_EXT_ENC_ZINT | (n_ext != 0 ? _Z_FLAG_Z_Z : 0))); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, oam->_ext_qos._val)); } if (has_timestamp_ext) { n_ext -= 1; _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, 0x02 | _Z_MSG_EXT_ENC_ZBUF | (n_ext != 0 ? _Z_FLAG_Z_Z : 0))); _Z_RETURN_IF_ERR(_z_timestamp_encode_ext(wbf, &oam->_ext_timestamp)); } switch (oam->_enc) { case _Z_OAM_BODY_UNIT: { // No body to encode } break; case _Z_OAM_BODY_ZINT: { _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, oam->_body._zint._val)); } break; case _Z_OAM_BODY_ZBUF: { _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &oam->_body._zbuf._val)); } break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_oam_decode_extensions(_z_msg_ext_t *extension, void *ctx) { _z_n_msg_oam_t *oam = (_z_n_msg_oam_t *)ctx; switch (_Z_EXT_FULL_ID(extension->_header)) { case _Z_MSG_EXT_ENC_ZINT | 0x01: { oam->_ext_qos._val = (uint8_t)extension->_body._zint._val; break; } case _Z_MSG_EXT_ENC_ZBUF | 0x02: { _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); return _z_timestamp_decode(&oam->_ext_timestamp, &zbf); } default: if (_Z_HAS_FLAG(extension->_header, _Z_MSG_EXT_FLAG_M)) { return _z_msg_ext_unknown_error(extension, 0x20); } } return _Z_RES_OK; } z_result_t _z_oam_decode(_z_n_msg_oam_t *oam, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_N_OAM"); // Decode ID _Z_RETURN_IF_ERR(_z_zint16_decode(&oam->_id, zbf)); // Initialize extensions with default values oam->_ext_qos = _Z_N_QOS_DEFAULT; oam->_ext_timestamp = _z_timestamp_null(); // Decode extensions if (_Z_HAS_FLAG(header, _Z_FLAG_N_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_decode_iter(zbf, _z_oam_decode_extensions, oam)) } // Decode payload switch (_Z_EXT_ENC(header)) { case _Z_MSG_EXT_ENC_UNIT: { oam->_enc = _Z_OAM_BODY_UNIT; return _Z_RES_OK; } break; case _Z_MSG_EXT_ENC_ZINT: { oam->_enc = _Z_OAM_BODY_ZINT; return _z_zint64_decode(&oam->_body._zint._val, zbf); } break; case _Z_MSG_EXT_ENC_ZBUF: { oam->_enc = _Z_OAM_BODY_ZBUF; return _z_slice_decode(&oam->_body._zbuf._val, zbf); } break; default: { _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } } } z_result_t _z_network_message_encode(_z_wbuf_t *wbf, const _z_network_message_t *msg) { switch (msg->_tag) { case _Z_N_DECLARE: { return _z_declare_encode(wbf, &msg->_body._declare); } break; #if Z_FEATURE_PUBLICATION == 1 case _Z_N_PUSH: { return _z_push_encode(wbf, &msg->_body._push); } break; #endif #if Z_FEATURE_QUERY == 1 case _Z_N_REQUEST: { return _z_request_encode(wbf, &msg->_body._request); } break; #endif #if Z_FEATURE_QUERYABLE == 1 case _Z_N_RESPONSE: { return _z_response_encode(wbf, &msg->_body._response); } break; case _Z_N_RESPONSE_FINAL: { return _z_response_final_encode(wbf, &msg->_body._response_final); } break; #endif #if Z_FEATURE_INTEREST == 1 case _Z_N_INTEREST: { return _z_n_interest_encode(wbf, &msg->_body._interest); } break; #endif case _Z_N_OAM: { return _z_oam_encode(wbf, &msg->_body._oam); } break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } } z_result_t _z_network_message_decode(_z_network_message_t *msg, _z_zbuf_t *zbf, _z_arc_slice_t *arcs, uintptr_t mapping) { uint8_t *header; *msg = (_z_network_message_t){0}; _Z_RETURN_IF_ERR(_z_uint8_decode_as_ref(&header, zbf)); switch (_Z_MID(*header)) { case _Z_MID_N_DECLARE: { msg->_tag = _Z_N_DECLARE; return _z_declare_decode(&msg->_body._declare, zbf, *header, mapping); } break; case _Z_MID_N_PUSH: { msg->_tag = _Z_N_PUSH; return _z_push_decode(&msg->_body._push, zbf, *header, arcs, mapping); } break; case _Z_MID_N_REQUEST: { msg->_tag = _Z_N_REQUEST; return _z_request_decode(&msg->_body._request, zbf, *header, arcs, mapping); } break; case _Z_MID_N_RESPONSE: { msg->_tag = _Z_N_RESPONSE; return _z_response_decode(&msg->_body._response, zbf, *header, arcs, mapping); } break; case _Z_MID_N_RESPONSE_FINAL: { msg->_tag = _Z_N_RESPONSE_FINAL; return _z_response_final_decode(&msg->_body._response_final, zbf, *header); } break; case _Z_MID_N_INTEREST: { msg->_tag = _Z_N_INTEREST; return _z_n_interest_decode(&msg->_body._interest, zbf, *header, mapping); } break; case _Z_MID_N_OAM: { msg->_tag = _Z_N_OAM; return _z_oam_decode(&msg->_body._oam, zbf, *header); } break; default: _Z_INFO("Unknown message type received: %d", _Z_MID(*header)); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } } ================================================ FILE: src/protocol/codec/serial.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/utils/checksum.h" #include "zenoh-pico/utils/encoding.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #define KIND_FIELD_LEN 1u #define LEN_FIELD_LEN 2u #define CRC32_LEN 4u size_t _z_serial_msg_serialize(uint8_t *dest, size_t dest_len, const uint8_t *src, size_t src_len, uint8_t header, uint8_t *tmp_buf, size_t tmp_buf_len) { size_t expected_size = src_len + KIND_FIELD_LEN + LEN_FIELD_LEN + CRC32_LEN; if (tmp_buf_len < expected_size) { _Z_DEBUG("tmp buffer too small: %zu < %zu", tmp_buf_len, expected_size); return SIZE_MAX; } uint32_t crc32 = _z_crc32(src, src_len); uint8_t crc_bytes[CRC32_LEN] = {(uint8_t)(crc32 & 0xFF), (uint8_t)((crc32 >> 8) & 0xFF), (uint8_t)((crc32 >> 16) & 0xFF), (uint8_t)((crc32 >> 24) & 0xFF)}; uint16_t wire_size = (uint16_t)src_len; uint8_t size_bytes[LEN_FIELD_LEN] = {(uint8_t)(wire_size & 0xFF), (uint8_t)((wire_size >> 8) & 0xFF)}; uint8_t *tmp_buf_ptr = tmp_buf; tmp_buf_ptr[0] = header; tmp_buf_ptr += sizeof(header); memcpy(tmp_buf_ptr, size_bytes, sizeof(size_bytes)); tmp_buf_ptr += sizeof(size_bytes); memcpy(tmp_buf_ptr, src, src_len); tmp_buf_ptr += src_len; memcpy(tmp_buf_ptr, crc_bytes, sizeof(crc_bytes)); tmp_buf_ptr += sizeof(crc_bytes); size_t total_len = _z_ptr_u8_diff(tmp_buf_ptr, tmp_buf); size_t ret = _z_cobs_encode(tmp_buf, total_len, dest); if (ret + 1 > dest_len) { _Z_DEBUG("destination buffer too small"); return SIZE_MAX; } dest[ret] = 0x00; return ret + 1u; } size_t _z_serial_msg_deserialize(const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, uint8_t *header, uint8_t *tmp_buf, size_t tmp_buf_len) { if (tmp_buf_len < src_len) { _Z_DEBUG("tmp_buf too small"); return SIZE_MAX; } size_t decoded_size = _z_cobs_decode(src, src_len, tmp_buf); if (decoded_size < KIND_FIELD_LEN + LEN_FIELD_LEN + CRC32_LEN) { _Z_DEBUG("decoded frame too small"); return SIZE_MAX; } uint8_t *tmp_buf_ptr = tmp_buf; *header = tmp_buf_ptr[0]; tmp_buf_ptr += sizeof(uint8_t); uint16_t wire_size = (uint16_t)(tmp_buf_ptr[0] | (tmp_buf_ptr[1] << 8)); tmp_buf_ptr += sizeof(uint16_t); size_t expected_size = wire_size + KIND_FIELD_LEN + LEN_FIELD_LEN + CRC32_LEN; if (expected_size != decoded_size) { _Z_DEBUG("wire size mismatch: %zu != %zu", expected_size, decoded_size); return SIZE_MAX; } if (dst_len < wire_size) { _Z_DEBUG("destination buffer too small: %zu < %u", dst_len, wire_size); return SIZE_MAX; } if (wire_size != 0) { memcpy(dst, tmp_buf_ptr, wire_size); tmp_buf_ptr += wire_size; } uint32_t received_crc = (uint32_t)(tmp_buf_ptr[0] | (tmp_buf_ptr[1] << 8) | (tmp_buf_ptr[2] << 16) | (tmp_buf_ptr[3] << 24)); uint32_t computed_crc = _z_crc32(dst, wire_size); if (received_crc != computed_crc) { _Z_DEBUG("CRC mismatch. Received: 0x%08" PRIu32 ", Computed: 0x%08" PRIu32, received_crc, computed_crc); return SIZE_MAX; } return wire_size; } ================================================ FILE: src/protocol/codec/transport.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/transport.h" #include #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/definitions/core.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" uint8_t _z_whatami_to_uint8(z_whatami_t whatami) { return (whatami >> 1) & 0x03; // get set bit index; only first 3 bits can be set } z_whatami_t _z_whatami_from_uint8(uint8_t b) { return 1 << (b & 0x03); // convert set bit idx into bitmask } /*------------------ Join Message ------------------*/ z_result_t _z_join_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_join_t *msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_T_JOIN"); _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, msg->_version)); uint8_t cbyte = 0; cbyte |= _z_whatami_to_uint8(msg->_whatami); uint8_t zidlen = _z_id_len(msg->_zid); cbyte |= (uint8_t)(((zidlen - 1) & 0x0F) << 4); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)); _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, msg->_zid.id, 0, zidlen)); if (_Z_HAS_FLAG(header, _Z_FLAG_T_JOIN_S)) { cbyte = msg->_seq_num_res & 0x03; cbyte = (uint8_t)(cbyte | ((msg->_req_id_res & 0x03) << 2)); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)); _Z_RETURN_IF_ERR(_z_uint16_encode(wbf, msg->_batch_size)); } if (_Z_HAS_FLAG(header, _Z_FLAG_T_JOIN_T) == true) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_lease / 1000)); } else { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_lease)); } _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_next_sn._val._plain._reliable)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_next_sn._val._plain._best_effort)); #if Z_FEATURE_FRAGMENTATION == 1 bool has_patch = msg->_patch != _Z_NO_PATCH; #else bool has_patch = false; #endif if (msg->_next_sn._is_qos) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ID_JOIN_QOS | _Z_MSG_EXT_MORE(has_patch))); size_t len = 0; for (uint8_t i = 0; (i < Z_PRIORITIES_NUM) && (ret == _Z_RES_OK); i++) { len += _z_zint_len(msg->_next_sn._val._qos[i]._reliable) + _z_zint_len(msg->_next_sn._val._qos[i]._best_effort); } _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, len)); for (uint8_t i = 0; (i < Z_PRIORITIES_NUM) && (ret == _Z_RES_OK); i++) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_next_sn._val._qos[i]._reliable)); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_next_sn._val._qos[i]._best_effort)); } } else { _Z_DEBUG("Attempted to serialize QoS-SN extension, but the header extension flag was unset"); ret |= _Z_ERR_MESSAGE_SERIALIZATION_FAILED; } } #if Z_FEATURE_FRAGMENTATION == 1 if (has_patch) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ID_JOIN_PATCH)); _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, msg->_patch)); } else { _Z_DEBUG("Attempted to serialize Patch extension, but the header extension flag was unset"); ret |= _Z_ERR_MESSAGE_SERIALIZATION_FAILED; } } #endif return ret; } z_result_t _z_join_decode_ext(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; _z_t_msg_join_t *msg = (_z_t_msg_join_t *)ctx; if (_Z_EXT_FULL_ID(extension->_header) == _Z_MSG_EXT_ID_JOIN_QOS) { msg->_next_sn._is_qos = true; _z_zbuf_t zbf = _z_slice_as_zbuf(extension->_body._zbuf._val); for (int i = 0; (ret == _Z_RES_OK) && (i < Z_PRIORITIES_NUM); ++i) { ret |= _z_zsize_decode(&msg->_next_sn._val._qos[i]._reliable, &zbf); ret |= _z_zsize_decode(&msg->_next_sn._val._qos[i]._best_effort, &zbf); } #if Z_FEATURE_FRAGMENTATION == 1 } else if (_Z_EXT_FULL_ID(extension->_header) == _Z_MSG_EXT_ID_JOIN_PATCH) { msg->_patch = (uint8_t)extension->_body._zint._val; #endif } else if (_Z_MSG_EXT_IS_MANDATORY(extension->_header)) { _Z_ERROR_LOG(_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN); ret = _Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN; } return ret; } z_result_t _z_join_decode(_z_t_msg_join_t *msg, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_T_JOIN"); z_result_t ret = _Z_RES_OK; *msg = (_z_t_msg_join_t){0}; ret |= _z_uint8_decode(&msg->_version, zbf); uint8_t cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_whatami = _z_whatami_from_uint8(cbyte); uint8_t zidlen = ((cbyte & 0xF0) >> 4) + (uint8_t)1; msg->_zid = _z_id_empty(); if (ret == _Z_RES_OK) { if (_z_zbuf_len(zbf) >= zidlen) { _z_zbuf_read_bytes(zbf, msg->_zid.id, 0, zidlen); } else { _Z_INFO("Invalid zid length received"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } if (ret == _Z_RES_OK) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_JOIN_S) == true) { cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_seq_num_res = (cbyte & 0x03); msg->_req_id_res = ((cbyte >> 2) & 0x03); ret |= _z_uint16_decode(&msg->_batch_size, zbf); } else { msg->_seq_num_res = _Z_DEFAULT_RESOLUTION_SIZE; msg->_req_id_res = _Z_DEFAULT_RESOLUTION_SIZE; msg->_batch_size = _Z_DEFAULT_MULTICAST_BATCH_SIZE; } } if (ret == _Z_RES_OK) { ret |= _z_zsize_decode(&msg->_lease, zbf); if (_Z_HAS_FLAG(header, _Z_FLAG_T_JOIN_T) == true) { msg->_lease = msg->_lease * 1000; } } if (ret == _Z_RES_OK) { msg->_next_sn._is_qos = false; ret |= _z_zsize_decode(&msg->_next_sn._val._plain._reliable, zbf); ret |= _z_zsize_decode(&msg->_next_sn._val._plain._best_effort, zbf); } #if Z_FEATURE_FRAGMENTATION == 1 msg->_patch = _Z_NO_PATCH; #endif if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { ret |= _z_msg_ext_decode_iter(zbf, _z_join_decode_ext, msg); } return ret; } /*------------------ Init Message ------------------*/ z_result_t _z_init_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_init_t *msg) { _Z_DEBUG("Encoding _Z_MID_T_INIT"); z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, msg->_version)) uint8_t cbyte = 0; cbyte |= _z_whatami_to_uint8(msg->_whatami); uint8_t zidlen = _z_id_len(msg->_zid); cbyte |= (uint8_t)(((zidlen - 1) & 0x0F) << 4); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)) _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, msg->_zid.id, 0, zidlen)) if (_Z_HAS_FLAG(header, _Z_FLAG_T_INIT_S) == true) { cbyte = msg->_seq_num_res & 0x03; cbyte = (uint8_t)(cbyte | ((msg->_req_id_res & 0x03) << 2)); _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, cbyte)) _Z_RETURN_IF_ERR(_z_uint16_encode(wbf, msg->_batch_size)) } if (_Z_HAS_FLAG(header, _Z_FLAG_T_INIT_A) == true) { _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &msg->_cookie)) } #if Z_FEATURE_FRAGMENTATION == 1 if (msg->_patch != _Z_NO_PATCH) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ID_JOIN_PATCH)); _Z_RETURN_IF_ERR(_z_zint64_encode(wbf, msg->_patch)); } else { _Z_DEBUG("Attempted to serialize Patch extension, but the header extension flag was unset"); ret |= _Z_ERR_MESSAGE_SERIALIZATION_FAILED; } } #endif return ret; } z_result_t _z_init_decode_ext(_z_msg_ext_t *extension, void *ctx) { _ZP_UNUSED(ctx); z_result_t ret = _Z_RES_OK; if (false) { #if Z_FEATURE_FRAGMENTATION == 1 } else if (_Z_EXT_FULL_ID(extension->_header) == _Z_MSG_EXT_ID_INIT_PATCH) { _z_t_msg_init_t *msg = (_z_t_msg_init_t *)ctx; msg->_patch = (uint8_t)extension->_body._zint._val; #endif } else if (_Z_MSG_EXT_IS_MANDATORY(extension->_header)) { _Z_ERROR_LOG(_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN); ret = _Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN; } return ret; } z_result_t _z_init_decode(_z_t_msg_init_t *msg, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_T_INIT"); *msg = (_z_t_msg_init_t){0}; z_result_t ret = _Z_RES_OK; ret |= _z_uint8_decode(&msg->_version, zbf); uint8_t cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_zid = _z_id_empty(); if (ret == _Z_RES_OK) { msg->_whatami = _z_whatami_from_uint8(cbyte); uint8_t zidlen = ((cbyte & 0xF0) >> 4) + (uint8_t)1; if (_z_zbuf_len(zbf) >= zidlen) { _z_zbuf_read_bytes(zbf, msg->_zid.id, 0, zidlen); } else { _Z_INFO("Invalid zid length received"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } } if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_INIT_S) == true)) { cbyte = 0; ret |= _z_uint8_decode(&cbyte, zbf); msg->_seq_num_res = (cbyte & 0x03); msg->_req_id_res = ((cbyte >> 2) & 0x03); ret |= _z_uint16_decode(&msg->_batch_size, zbf); } else { msg->_seq_num_res = _Z_DEFAULT_RESOLUTION_SIZE; msg->_req_id_res = _Z_DEFAULT_RESOLUTION_SIZE; msg->_batch_size = _Z_DEFAULT_UNICAST_BATCH_SIZE; } if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_INIT_A) == true)) { ret |= _z_slice_decode(&msg->_cookie, zbf); } else { msg->_cookie = _z_slice_null(); } #if Z_FEATURE_FRAGMENTATION == 1 msg->_patch = _Z_NO_PATCH; #endif if ((ret == _Z_RES_OK) && _Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { ret |= _z_msg_ext_decode_iter(zbf, _z_init_decode_ext, msg); } return ret; } /*------------------ Open Message ------------------*/ z_result_t _z_open_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_open_t *msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_T_OPEN"); if (_Z_HAS_FLAG(header, _Z_FLAG_T_OPEN_T) == true) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_lease / 1000)) } else { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_lease)) } _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_initial_sn)) if (_Z_HAS_FLAG(header, _Z_FLAG_T_OPEN_A) == false) { _Z_RETURN_IF_ERR(_z_slice_encode(wbf, &msg->_cookie)) } return ret; } z_result_t _z_open_decode(_z_t_msg_open_t *msg, _z_zbuf_t *zbf, uint8_t header) { _Z_DEBUG("Decoding _Z_MID_T_OPEN"); z_result_t ret = _Z_RES_OK; *msg = (_z_t_msg_open_t){0}; ret |= _z_zsize_decode(&msg->_lease, zbf); if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_OPEN_T) == true)) { msg->_lease = msg->_lease * 1000; } ret |= _z_zsize_decode(&msg->_initial_sn, zbf); if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_OPEN_A) == false)) { ret |= _z_slice_decode(&msg->_cookie, zbf); if (ret != _Z_RES_OK) { msg->_cookie = _z_slice_null(); } } else { msg->_cookie = _z_slice_null(); } if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true)) { ret |= _z_msg_ext_skip_non_mandatories(zbf, 0x02); } return ret; } /*------------------ Close Message ------------------*/ z_result_t _z_close_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_close_t *msg) { (void)(header); z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_T_CLOSE"); ret |= _z_wbuf_write(wbf, msg->_reason); return ret; } z_result_t _z_close_decode(_z_t_msg_close_t *msg, _z_zbuf_t *zbf, uint8_t header) { (void)(header); z_result_t ret = _Z_RES_OK; *msg = (_z_t_msg_close_t){0}; _Z_DEBUG("Decoding _Z_MID_T_CLOSE"); ret |= _z_uint8_decode(&msg->_reason, zbf); return ret; } /*------------------ Keep Alive Message ------------------*/ z_result_t _z_keep_alive_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_keep_alive_t *msg) { (void)(wbf); (void)(header); (void)(msg); z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_MID_T_KEEP_ALIVE"); return ret; } z_result_t _z_keep_alive_decode(_z_t_msg_keep_alive_t *msg, _z_zbuf_t *zbf, uint8_t header) { (void)(msg); (void)(zbf); (void)(header); *msg = (_z_t_msg_keep_alive_t){0}; z_result_t ret = _Z_RES_OK; _Z_DEBUG("Decoding _Z_MID_T_KEEP_ALIVE"); if (_Z_HAS_FLAG(header, _Z_FLAG_Z_Z)) { ret |= _z_msg_ext_skip_non_mandatories(zbf, 0x03); } return ret; } /*------------------ Frame Message ------------------*/ z_result_t _z_frame_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_frame_t *msg) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_sn)) if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { _Z_ERROR_RETURN(_Z_ERR_MESSAGE_SERIALIZATION_FAILED); } if (msg->_payload != NULL) { _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, _z_zbuf_get_rptr(msg->_payload), 0, _z_zbuf_len(msg->_payload))); } return _Z_RES_OK; } z_result_t _z_frame_decode(_z_t_msg_frame_t *msg, _z_zbuf_t *zbf, uint8_t header) { *msg = (_z_t_msg_frame_t){0}; _Z_RETURN_IF_ERR(_z_zsize_decode(&msg->_sn, zbf)); if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z)) { _Z_RETURN_IF_ERR(_z_msg_ext_skip_non_mandatories(zbf, 0x04)); } // Note payload msg->_payload = zbf; return _Z_RES_OK; } /*------------------ Fragment Message ------------------*/ z_result_t _z_fragment_encode(_z_wbuf_t *wbf, uint8_t header, const _z_t_msg_fragment_t *msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_TRANSPORT_FRAGMENT"); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, msg->_sn)) if (msg->first) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ID_FRAGMENT_FIRST | _Z_MSG_EXT_MORE(msg->drop))); } else { _Z_DEBUG("Attempted to serialize Start extension, but the header extension flag was unset"); ret |= _Z_ERR_MESSAGE_SERIALIZATION_FAILED; } } if (msg->drop) { if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true) { _Z_RETURN_IF_ERR(_z_uint8_encode(wbf, _Z_MSG_EXT_ID_FRAGMENT_DROP)); } else { _Z_DEBUG("Attempted to serialize Stop extension, but the header extension flag was unset"); ret |= _Z_ERR_MESSAGE_SERIALIZATION_FAILED; } } if (_z_slice_check(&msg->_payload)) { _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, msg->_payload.start, 0, msg->_payload.len)); } return ret; } z_result_t _z_fragment_decode_ext(_z_msg_ext_t *extension, void *ctx) { z_result_t ret = _Z_RES_OK; _z_t_msg_fragment_t *msg = (_z_t_msg_fragment_t *)ctx; if (_Z_EXT_FULL_ID(extension->_header) == _Z_MSG_EXT_ID_FRAGMENT_FIRST) { msg->first = true; } else if (_Z_EXT_FULL_ID(extension->_header) == _Z_MSG_EXT_ID_FRAGMENT_DROP) { msg->drop = true; } else if (_Z_MSG_EXT_IS_MANDATORY(extension->_header)) { _Z_ERROR_LOG(_Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN); ret = _Z_ERR_MESSAGE_EXTENSION_MANDATORY_AND_UNKNOWN; } return ret; } z_result_t _z_fragment_decode(_z_t_msg_fragment_t *msg, _z_zbuf_t *zbf, uint8_t header) { z_result_t ret = _Z_RES_OK; *msg = (_z_t_msg_fragment_t){0}; _Z_DEBUG("Decoding _Z_TRANSPORT_FRAGMENT"); ret |= _z_zsize_decode(&msg->_sn, zbf); msg->first = false; msg->drop = false; if ((ret == _Z_RES_OK) && (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true)) { ret |= _z_msg_ext_decode_iter(zbf, _z_fragment_decode_ext, msg); } msg->_payload = _z_slice_alias_buf((uint8_t *)_z_zbuf_start(zbf), _z_zbuf_len(zbf)); zbf->_ios._r_pos = zbf->_ios._w_pos; return ret; } /*------------------ Transport Extensions Message ------------------*/ z_result_t _z_extensions_encode(_z_wbuf_t *wbf, uint8_t header, const _z_msg_ext_vec_t *v_ext) { (void)(header); z_result_t ret = _Z_RES_OK; _Z_DEBUG("Encoding _Z_TRANSPORT_EXTENSIONS"); if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true) { ret |= _z_msg_ext_vec_encode(wbf, v_ext); } return ret; } z_result_t _z_extensions_decode(_z_msg_ext_vec_t *v_ext, _z_zbuf_t *zbf, uint8_t header) { (void)(header); z_result_t ret = _Z_RES_OK; _Z_DEBUG("Decoding _Z_TRANSPORT_EXTENSIONS"); if (_Z_HAS_FLAG(header, _Z_FLAG_T_Z) == true) { ret |= _z_msg_ext_vec_decode(v_ext, zbf); } else { _z_msg_ext_vec_reset(v_ext); } return ret; } #if defined Z_TEST_HOOKS static _z_transport_message_encode_override_fn _z_transport_message_encode_override = NULL; void _z_transport_set_message_encode_override(_z_transport_message_encode_override_fn fn) { _z_transport_message_encode_override = fn; } #endif /*------------------ Transport Message ------------------*/ z_result_t _z_transport_message_encode(_z_wbuf_t *wbf, const _z_transport_message_t *msg) { #if defined(Z_TEST_HOOKS) if (_z_transport_message_encode_override != NULL) { bool handled = false; z_result_t override_ret = _z_transport_message_encode_override(wbf, msg, &handled); if (handled) { return override_ret; } } #endif _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, msg->_header)) switch (_Z_MID(msg->_header)) { case _Z_MID_T_FRAME: { return _z_frame_encode(wbf, msg->_header, &msg->_body._frame); } break; case _Z_MID_T_FRAGMENT: { return _z_fragment_encode(wbf, msg->_header, &msg->_body._fragment); } break; case _Z_MID_T_KEEP_ALIVE: { return _z_keep_alive_encode(wbf, msg->_header, &msg->_body._keep_alive); } break; #if Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_MID_T_JOIN: { return _z_join_encode(wbf, msg->_header, &msg->_body._join); } break; #endif case _Z_MID_T_INIT: { return _z_init_encode(wbf, msg->_header, &msg->_body._init); } break; case _Z_MID_T_OPEN: { return _z_open_encode(wbf, msg->_header, &msg->_body._open); } break; case _Z_MID_T_CLOSE: { return _z_close_encode(wbf, msg->_header, &msg->_body._close); } break; default: { _Z_INFO("WARNING: Trying to encode session message with unknown ID(%d)", _Z_MID(msg->_header)); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_TRANSPORT_UNKNOWN); } break; } } z_result_t _z_transport_message_decode(_z_transport_message_t *msg, _z_zbuf_t *zbf) { _Z_RETURN_IF_ERR(_z_uint8_decode(&msg->_header, zbf)); // Decode the header uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_T_FRAME: { return _z_frame_decode(&msg->_body._frame, zbf, msg->_header); } break; case _Z_MID_T_FRAGMENT: { return _z_fragment_decode(&msg->_body._fragment, zbf, msg->_header); } break; case _Z_MID_T_KEEP_ALIVE: { return _z_keep_alive_decode(&msg->_body._keep_alive, zbf, msg->_header); } break; case _Z_MID_T_JOIN: { return _z_join_decode(&msg->_body._join, zbf, msg->_header); } break; case _Z_MID_T_INIT: { return _z_init_decode(&msg->_body._init, zbf, msg->_header); } break; case _Z_MID_T_OPEN: { return _z_open_decode(&msg->_body._open, zbf, msg->_header); } break; case _Z_MID_T_CLOSE: { return _z_close_decode(&msg->_body._close, zbf, msg->_header); } break; default: { _Z_INFO("WARNING: Trying to decode session message with unknown ID(0x%x) (header=0x%x)", mid, msg->_header); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_TRANSPORT_UNKNOWN); } break; } } ================================================ FILE: src/protocol/codec.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec.h" #include #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" /*------------------ uint8 -------------------*/ z_result_t _z_consolidation_mode_encode(_z_wbuf_t *wbf, z_consolidation_mode_t en) { return _z_zsize_encode(wbf, en); } z_result_t _z_consolidation_mode_decode(z_consolidation_mode_t *en, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; _z_zint_t tmp; ret |= _z_zsize_decode(&tmp, zbf); *en = tmp; return ret; } z_result_t _z_query_target_encode(_z_wbuf_t *wbf, z_query_target_t en) { return _z_zsize_encode(wbf, en); } z_result_t _z_query_target_decode(z_query_target_t *en, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; _z_zint_t tmp; ret |= _z_zsize_decode(&tmp, zbf); *en = tmp; return ret; } z_result_t _z_whatami_encode(_z_wbuf_t *wbf, z_whatami_t en) { return _z_zsize_encode(wbf, _z_whatami_to_uint8(en)); } z_result_t _z_whatami_decode(z_whatami_t *en, _z_zbuf_t *zbf) { z_result_t ret = _Z_RES_OK; _z_zint_t tmp; ret |= _z_zsize_decode(&tmp, zbf); *en = _z_whatami_from_uint8((uint8_t)tmp); return ret; } z_result_t _z_uint8_encode(_z_wbuf_t *wbf, uint8_t u8) { return _z_wbuf_write(wbf, u8); } z_result_t _z_uint8_decode(uint8_t *u8, _z_zbuf_t *zbf) { if (!_z_zbuf_can_read(zbf)) { _Z_WARN("Not enough bytes to read"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *u8 = _z_zbuf_read(zbf); return _Z_RES_OK; } z_result_t _z_uint8_decode_as_ref(uint8_t **u8, _z_zbuf_t *zbf) { if (!_z_zbuf_can_read(zbf)) { _Z_WARN("Not enough bytes to read"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *u8 = _z_zbuf_read_as_ref(zbf); return _Z_RES_OK; } z_result_t _z_uint16_encode(_z_wbuf_t *wbf, uint16_t val) { _Z_RETURN_IF_ERR(_z_wbuf_write(wbf, _z_get_u16_lsb(val))); return _z_wbuf_write(wbf, _z_get_u16_msb(val)); } z_result_t _z_uint16_decode(uint16_t *u16, _z_zbuf_t *zbf) { if (_z_zbuf_len(zbf) < sizeof(uint16_t)) { _Z_WARN("Not enough bytes to read"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *u16 = _z_zbuf_read(zbf) + (uint16_t)(_z_zbuf_read(zbf) << 8); _z_host_u16_from_le_u16(u16); return _Z_RES_OK; } /*------------------ z_zint ------------------*/ // Zint is a variable int composed of up to 9 bytes. // The msb of the 8 first bytes has meaning: (1: the zint continue, 0: end of the zint) #define VLE_LEN 9 #define VLE_LEN1_MASK (UINT64_MAX << (7 * 1)) #define VLE_LEN2_MASK (UINT64_MAX << (7 * 2)) #define VLE_LEN3_MASK (UINT64_MAX << (7 * 3)) #define VLE_LEN4_MASK (UINT64_MAX << (7 * 4)) #define VLE_LEN5_MASK (UINT64_MAX << (7 * 5)) #define VLE_LEN6_MASK (UINT64_MAX << (7 * 6)) #define VLE_LEN7_MASK (UINT64_MAX << (7 * 7)) #define VLE_LEN8_MASK (UINT64_MAX << (7 * 8)) uint8_t _z_zint_len(uint64_t v) { if ((v & VLE_LEN1_MASK) == 0) { return 1; } else if ((v & VLE_LEN2_MASK) == 0) { return 2; } else if ((v & VLE_LEN3_MASK) == 0) { return 3; } else if ((v & VLE_LEN4_MASK) == 0) { return 4; } else if ((v & VLE_LEN5_MASK) == 0) { return 5; } else if ((v & VLE_LEN6_MASK) == 0) { return 6; } else if ((v & VLE_LEN7_MASK) == 0) { return 7; } else if ((v & VLE_LEN8_MASK) == 0) { return 8; } else { return 9; } } uint8_t _z_zint64_encode_buf(uint8_t *buf, uint64_t v) { uint64_t lv = v; uint8_t len = 0; size_t start = 0; while ((lv & VLE_LEN1_MASK) != 0) { uint8_t c = (uint8_t)((lv & 0x7f) | 0x80); buf[start++] = c; len++; lv = lv >> (uint64_t)7; } if (len != VLE_LEN) { uint8_t c = (lv & 0xff); buf[start++] = c; } return (uint8_t)start; } z_result_t _z_zint64_encode(_z_wbuf_t *wbf, uint64_t v) { uint8_t buf[VLE_LEN]; size_t len = _z_zint64_encode_buf(buf, v); return _z_wbuf_write_bytes(wbf, buf, 0, len); } z_result_t _z_zint64_decode_with_reader(uint64_t *zint, __z_single_byte_reader_t reader, void *context) { *zint = 0; uint8_t b = 0; _Z_RETURN_IF_ERR(reader(&b, context)); uint8_t i = 0; while (((b & 0x80) != 0) && (i != 7 * (VLE_LEN - 1))) { *zint = *zint | ((uint64_t)(b & 0x7f)) << i; _Z_RETURN_IF_ERR(reader(&b, context)); i = i + (uint8_t)7; } *zint = *zint | ((uint64_t)b << i); return _Z_RES_OK; } z_result_t _z_zsize_decode_with_reader(_z_zint_t *zint, __z_single_byte_reader_t reader, void *context) { uint64_t i = 0; z_result_t res = _z_zint64_decode_with_reader(&i, reader, context); if (res != _Z_RES_OK || i > SIZE_MAX) { _Z_INFO("Reader decode failed"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); res = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } else { *zint = (_z_zint_t)i; } return res; } z_result_t _z_uint8_decode_reader(uint8_t *zint, void *context) { return _z_uint8_decode(zint, (_z_zbuf_t *)context); } z_result_t _z_zint64_decode(uint64_t *zint, _z_zbuf_t *zbf) { *zint = 0; uint8_t b = 0; _Z_RETURN_IF_ERR(_z_uint8_decode(&b, zbf)); uint8_t i = 0; while (((b & 0x80) != 0) && (i != 7 * (VLE_LEN - 1))) { *zint = *zint | ((uint64_t)(b & 0x7f)) << i; _Z_RETURN_IF_ERR(_z_uint8_decode(&b, zbf)); i = i + (uint8_t)7; } *zint = *zint | ((uint64_t)b << i); return _Z_RES_OK; } z_result_t _z_zint16_decode(uint16_t *zint, _z_zbuf_t *zbf) { uint64_t buf; _Z_RETURN_IF_ERR(_z_zint64_decode(&buf, zbf)); if (buf > UINT16_MAX) { _Z_WARN("Invalid zint16 value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *zint = (uint16_t)buf; return _Z_RES_OK; } z_result_t _z_zint32_decode(uint32_t *zint, _z_zbuf_t *zbf) { uint64_t buf; _Z_RETURN_IF_ERR(_z_zint64_decode(&buf, zbf)); if (buf > UINT32_MAX) { _Z_WARN("Invalid zint32 value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *zint = (uint32_t)buf; return _Z_RES_OK; } z_result_t _z_zsize_decode(_z_zint_t *zint, _z_zbuf_t *zbf) { uint64_t buf; _Z_RETURN_IF_ERR(_z_zint64_decode(&buf, zbf)); if (buf > SIZE_MAX) { _Z_WARN("Invalid zsize value decoded"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } *zint = (_z_zint_t)buf; return _Z_RES_OK; } /*------------------ uint8_array ------------------*/ z_result_t _z_buf_encode(_z_wbuf_t *wbf, const uint8_t *buf, size_t len) { z_result_t ret = _Z_RES_OK; if ((wbf->_expansion_step != 0) && (len > Z_ZID_LENGTH)) { ret |= _z_wbuf_wrap_bytes(wbf, buf, 0, len); } else { ret |= _z_wbuf_write_bytes(wbf, buf, 0, len); } return ret; } z_result_t _z_slice_encode(_z_wbuf_t *wbf, const _z_slice_t *bs) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, bs->len)); return _z_slice_val_encode(wbf, bs); } z_result_t _z_slices_encode(_z_wbuf_t *wbf, const _z_slice_t *bs, size_t num_slices) { size_t total_len = 0; for (size_t i = 0; i < num_slices; ++i) { total_len += bs[i].len; } _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, total_len)); for (size_t i = 0; i < num_slices; ++i) { _Z_RETURN_IF_ERR(_z_slice_val_encode(wbf, &bs[i])); } return _Z_RES_OK; } z_result_t _z_bytes_decode(_z_bytes_t *bs, _z_zbuf_t *zbf, _z_arc_slice_t *arcs) { // Decode slice _z_slice_t s; _Z_RETURN_IF_ERR(_z_slice_decode(&s, zbf)); // Calc offset size_t offset = _z_ptr_u8_diff(s.start, _z_slice_simple_rc_value(&zbf->_slice)->start); // Get ownership of subslice *arcs = _z_arc_slice_wrap_slice_rc(&zbf->_slice, offset, s.len); _z_bytes_alias_arc_slice(bs, arcs); return _Z_RES_OK; } static inline z_result_t _z_bytes_encode_val(_z_wbuf_t *wbf, const _z_bytes_t *bs) { z_result_t ret = _Z_RES_OK; for (size_t i = 0; i < _z_bytes_num_slices(bs); ++i) { const _z_arc_slice_t *arc_s = _z_bytes_get_slice(bs, i); _Z_RETURN_IF_ERR(_z_buf_encode(wbf, _z_arc_slice_data(arc_s), _z_arc_slice_len(arc_s))) } return ret; } z_result_t _z_bytes_encode(_z_wbuf_t *wbf, const _z_bytes_t *bs) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, _z_bytes_len(bs))) return _z_bytes_encode_val(wbf, bs); } /*------------------ string with null terminator ------------------*/ z_result_t _z_str_encode(_z_wbuf_t *wbf, const char *s) { size_t len = strlen(s); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, len)) // Note that this does not put the string terminator on the wire. return _z_wbuf_write_bytes(wbf, (const uint8_t *)s, 0, len); } z_result_t _z_str_decode(char **str, _z_zbuf_t *zbf) { _z_zint_t len = 0; z_result_t ret = _z_zsize_decode(&len, zbf); if (ret != _Z_RES_OK) { *str = NULL; return ret; } // Check if we have enough bytes to read if (_z_zbuf_len(zbf) < len) { _Z_WARN("Not enough bytes to read"); *str = NULL; _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } // Allocate space for the null terminated string char *tmp = (char *)z_malloc(len + (size_t)1); if (tmp == NULL) { *str = NULL; _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Read and store string tmp[len] = '\0'; _z_zbuf_read_bytes(zbf, (uint8_t *)tmp, 0, len); *str = tmp; return _Z_RES_OK; } z_result_t _z_string_encode(_z_wbuf_t *wbf, const _z_string_t *s) { _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, _z_string_len(s))) // Note that this does not put the string terminator on the wire. return _z_wbuf_write_bytes(wbf, (const uint8_t *)_z_string_data(s), 0, _z_string_len(s)); } z_result_t _z_string_decode(_z_string_t *str, _z_zbuf_t *zbf) { _z_zint_t len = 0; // Decode string length _Z_RETURN_IF_ERR(_z_zsize_decode(&len, zbf)); // Check if we have enough bytes to read if (_z_zbuf_len(zbf) < len) { _Z_INFO("Not enough bytes to read"); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); } // Alias string *str = _z_string_alias_substr((const char *)_z_zbuf_get_rptr(zbf), len); _z_zbuf_set_rpos(zbf, _z_zbuf_get_rpos(zbf) + len); return _Z_RES_OK; } /*------------------ encoding ------------------*/ #define _Z_ENCODING_FLAG_S 0x01 size_t _z_encoding_len(const _z_encoding_t *en) { size_t en_len = _z_zint_len((uint32_t)(en->id) << 1); if (_z_string_check(&en->schema)) { en_len += _z_zint_len(_z_string_len(&en->schema)) + _z_string_len(&en->schema); } return en_len; } z_result_t _z_encoding_encode(_z_wbuf_t *wbf, const _z_encoding_t *en) { bool has_schema = _z_string_check(&en->schema); uint32_t id = (uint32_t)(en->id) << 1; if (has_schema) { id |= _Z_ENCODING_FLAG_S; } _Z_RETURN_IF_ERR(_z_zint32_encode(wbf, id)); if (has_schema) { _Z_RETURN_IF_ERR(_z_string_encode(wbf, &en->schema)); } return _Z_RES_OK; } z_result_t _z_encoding_decode(_z_encoding_t *en, _z_zbuf_t *zbf) { uint32_t id = 0; bool has_schema = false; _Z_RETURN_IF_ERR(_z_zint32_decode(&id, zbf)); if ((id & _Z_ENCODING_FLAG_S) != 0) { has_schema = true; } en->id = (uint16_t)(id >> 1); if (has_schema) { _Z_RETURN_IF_ERR(_z_string_decode(&en->schema, zbf)); } return _Z_RES_OK; } z_result_t _z_value_encode(_z_wbuf_t *wbf, const _z_value_t *value) { size_t total_len = _z_encoding_len(&value->encoding) + _z_bytes_len(&value->payload); _Z_RETURN_IF_ERR(_z_zsize_encode(wbf, total_len)); _Z_RETURN_IF_ERR(_z_encoding_encode(wbf, &value->encoding)); return _z_bytes_encode_val(wbf, &value->payload); } z_result_t _z_value_decode(_z_value_t *value, _z_zbuf_t *zbf) { _Z_RETURN_IF_ERR(_z_encoding_decode(&value->encoding, zbf)); _Z_RETURN_IF_ERR(_z_bytes_from_buf(&value->payload, (uint8_t *)_z_zbuf_start(zbf), _z_zbuf_len(zbf))); return _Z_RES_OK; } ================================================ FILE: src/protocol/config.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/config.h" #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/string.h" z_result_t _z_config_init(_z_config_t *ps) { _z_str_intmap_init(ps); return 0; } z_result_t _zp_config_insert(_z_config_t *ps, uint8_t key, const char *value) { z_result_t ret = _Z_RES_OK; const char *res = ""; if (key == Z_CONFIG_CONNECT_KEY) { res = _z_str_intmap_insert_push(ps, key, _z_str_clone(value)); } else { res = _z_str_intmap_insert(ps, key, _z_str_clone(value)); } if (strcmp(res, value) != 0) { _Z_ERROR_LOG(_Z_ERR_CONFIG_FAILED_INSERT); ret = _Z_ERR_CONFIG_FAILED_INSERT; } return ret; } z_result_t _zp_config_insert_string(_z_config_t *ps, uint8_t key, const _z_string_t *value) { z_result_t ret = _Z_RES_OK; char *str = _z_str_from_string_clone(value); char *res = _z_str_intmap_insert(ps, key, str); if (strcmp(res, str) != 0) { _Z_ERROR_LOG(_Z_ERR_CONFIG_FAILED_INSERT); ret = _Z_ERR_CONFIG_FAILED_INSERT; } z_free(str); return ret; } char *_z_config_get(const _z_config_t *ps, uint8_t key) { return _z_str_intmap_get(ps, key); } z_result_t _z_config_get_all(const _z_config_t *ps, _z_string_svec_t *locators, uint8_t key) { _z_list_t *cfg_list = _z_str_intmap_get_all(ps, key); while (cfg_list != NULL) { _z_int_void_map_entry_t *entry = (_z_int_void_map_entry_t *)_z_list_value(cfg_list); char *val = (char *)entry->_val; _z_string_t s = _z_string_copy_from_str(val); _Z_RETURN_IF_ERR(_z_string_svec_append(locators, &s, true)); cfg_list = _z_list_next(cfg_list); } return _Z_RES_OK; } z_result_t _z_config_get_i32_default(_z_config_t *config, uint8_t key, const char *default_val, int32_t *out) { const char *s = _z_config_get(config, key); if (s == NULL) { s = default_val; } return _z_str_parse_i32(s, out) ? _Z_RES_OK : _Z_ERR_CONFIG_INVALID_VALUE; } z_result_t _z_config_get_bool_default(_z_config_t *config, uint8_t key, const char *default_val, bool *out) { const char *s = _z_config_get(config, key); if (s == NULL) { s = default_val; } return _z_str_parse_bool(s, out) ? _Z_RES_OK : _Z_ERR_CONFIG_INVALID_VALUE; } /*------------------ int-string map ------------------*/ z_result_t _z_str_intmap_from_strn(_z_str_intmap_t *strint, const char *s, uint8_t argc, _z_str_intmapping_t argv[], size_t n) { z_result_t ret = _Z_RES_OK; *strint = _z_str_intmap_make(); // Check the string contains only the right const char *start = s; const char *end = &s[n - 1]; size_t curr_len = n; while (curr_len > 0) { const char *p_key_start = start; const char *p_key_end = memchr(p_key_start, INT_STR_MAP_KEYVALUE_SEPARATOR, curr_len); if (p_key_end != NULL) { // Verify the key is valid based on the provided mapping size_t p_key_len = _z_ptr_char_diff(p_key_end, p_key_start); bool found = false; uint8_t key = 0; for (uint8_t i = 0; i < argc; i++) { if (p_key_len != strlen(argv[i]._str)) { continue; } if (strncmp(p_key_start, argv[i]._str, p_key_len) != 0) { continue; } found = true; key = argv[i]._key; break; } if (found == false) { break; } // Read and populate the value const char *p_value_start = _z_cptr_char_offset(p_key_end, 1); size_t value_max_size = curr_len - _z_ptr_char_diff(p_value_start, start); const char *p_value_end = memchr(p_key_end, INT_STR_MAP_LIST_SEPARATOR, value_max_size); size_t p_value_len = 0; if (p_value_end == NULL) { p_value_end = end; p_value_len = value_max_size + 1; } else { p_value_len = _z_ptr_char_diff(p_value_end, p_value_start) + 1; } char *p_value = (char *)z_malloc(p_value_len); if (p_value != NULL) { _z_str_n_copy(p_value, p_value_start, p_value_len); _z_str_intmap_insert(strint, key, p_value); // Process next key value start = _z_cptr_char_offset(p_value_end, 1); curr_len = n - _z_ptr_char_diff(start, s); } else { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; } } } return ret; } z_result_t _z_str_intmap_from_str(_z_str_intmap_t *strint, const char *s, uint8_t argc, _z_str_intmapping_t argv[]) { return _z_str_intmap_from_strn(strint, s, argc, argv, strlen(s)); } size_t _z_str_intmap_strlen(const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]) { // Calculate the string length to allocate size_t len = 0; for (size_t i = 0; i < argc; i++) { char *v = _z_str_intmap_get(s, argv[i]._key); if (v != NULL) { if (len != (size_t)0) { len = len + (size_t)1; // List separator } len = len + strlen(argv[i]._str); // Key len = len + (size_t)1; // KeyValue separator len = len + strlen(v); // Value } } return len; } void _z_str_intmap_onto_str(char *dst, size_t dst_len, const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]) { if (dst == NULL || dst_len == 0) { return; } // Remaining length excluding '\0' size_t len = dst_len - (size_t)1; dst[0] = '\0'; for (size_t i = 0; i < argc; i++) { char *v = _z_str_intmap_get(s, argv[i]._key); if (v != NULL) { if (len > (size_t)0 && dst[0] != '\0') { _z_str_append(dst, INT_STR_MAP_LIST_SEPARATOR); // List separator len = len - (size_t)1; } size_t key_len = strnlen(argv[i]._str, len); if (len > (size_t)0) { size_t n = key_len < len ? key_len : len; // Flawfinder: ignore [CWE-120] (void)strncat(dst, argv[i]._str, n); // Key len = len - n; } if (len > (size_t)0) { _z_str_append(dst, INT_STR_MAP_KEYVALUE_SEPARATOR); // KeyValue separator len = len - (size_t)1; } size_t value_len = strnlen(v, len); if (len > (size_t)0) { size_t n = value_len < len ? value_len : len; // Flawfinder: ignore [CWE-120] (void)strncat(dst, v, n); // Value len = len - n; } } } } char *_z_str_intmap_to_str(const _z_str_intmap_t *s, uint8_t argc, _z_str_intmapping_t argv[]) { // Calculate the string length to allocate size_t len = _z_str_intmap_strlen(s, argc, argv) + (size_t)1; // Build the string char *dst = (char *)z_malloc(len); if (dst != NULL) { _z_str_intmap_onto_str(dst, len, s, argc, argv); } return dst; } ================================================ FILE: src/protocol/core.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/codec/core.h" #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/net/encoding.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/hash.h" #include "zenoh-pico/utils/logging.h" #define _Z_ID_LEN (16) const _z_id_t empty_id = {0}; uint8_t _z_id_len(_z_id_t id) { uint8_t len = _Z_ID_LEN; while (len > 0) { --len; if (id.id[len] != 0) { ++len; break; } } return len; } size_t _z_id_hash(const _z_id_t *id) { size_t hash = (size_t)_Z_FNV_OFFSET_BASIS; for (size_t i = 0; i < ZENOH_ID_SIZE; ++i) { hash ^= id->id[i]; hash *= _Z_FNV_PRIME; } return hash; } int _z_id_cmp(const _z_id_t *left, const _z_id_t *right) { for (size_t i = 0; i < ZENOH_ID_SIZE; ++i) { if (left->id[i] < right->id[i]) return -1; if (left->id[i] > right->id[i]) return 1; } return 0; } _z_ntp64_t _z_timestamp_ntp64_from_time(uint32_t seconds, uint32_t nanos) { const uint64_t FRAC_PER_SEC = (uint64_t)1 << 32; const uint64_t NANOS_PER_SEC = 1000000000; uint32_t fractions = (uint32_t)((uint64_t)nanos * FRAC_PER_SEC / NANOS_PER_SEC + 1); return ((uint64_t)seconds << 32) | fractions; } int _z_timestamp_cmp(const _z_timestamp_t *left, const _z_timestamp_t *right) { // Compare validity if (!left->valid && right->valid) return -1; if (left->valid && !right->valid) return 1; // Compare time if (left->time < right->time) return -1; if (left->time > right->time) return 1; // Compare IDs return _z_id_cmp(&left->id, &right->id); } _z_value_t _z_value_steal(_z_value_t *value) { _z_value_t ret = *value; *value = _z_value_null(); return ret; } z_result_t _z_value_copy(_z_value_t *dst, const _z_value_t *src) { *dst = _z_value_null(); _Z_RETURN_IF_ERR(_z_encoding_copy(&dst->encoding, &src->encoding)); _Z_CLEAN_RETURN_IF_ERR(_z_bytes_copy(&dst->payload, &src->payload), _z_encoding_clear(&dst->encoding)); return _Z_RES_OK; } z_result_t _z_hello_copy(_z_hello_t *dst, const _z_hello_t *src) { *dst = _z_hello_null(); _Z_RETURN_IF_ERR(_z_string_svec_copy(&dst->_locators, &src->_locators, true)); dst->_version = src->_version; dst->_whatami = src->_whatami; memcpy(&dst->_zid.id, &src->_zid.id, _Z_ID_LEN); return _Z_RES_OK; } z_result_t _z_hello_move(_z_hello_t *dst, _z_hello_t *src) { *dst = *src; *src = _z_hello_null(); return _Z_RES_OK; } z_result_t _z_value_move(_z_value_t *dst, _z_value_t *src) { *dst = _z_value_null(); _Z_RETURN_IF_ERR(_z_bytes_move(&dst->payload, &src->payload)); _Z_CLEAN_RETURN_IF_ERR(_z_encoding_move(&dst->encoding, &src->encoding), _z_value_clear(dst)); return _Z_RES_OK; } size_t _z_entity_global_id_hash(const _z_entity_global_id_t *e) { size_t hash = _z_id_hash(&e->zid); for (size_t i = 0; i < sizeof(e->eid); ++i) { hash ^= ((uint8_t *)&e->eid)[i]; hash *= _Z_FNV_PRIME; } return hash; } z_result_t _z_source_info_copy(_z_source_info_t *dst, const _z_source_info_t *src) { dst->_source_id = src->_source_id; dst->_source_sn = src->_source_sn; return _Z_RES_OK; } z_result_t _z_source_info_move(_z_source_info_t *dst, _z_source_info_t *src) { dst->_source_id = src->_source_id; dst->_source_sn = src->_source_sn; return _Z_RES_OK; } ================================================ FILE: src/protocol/definitions/declarations.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/declarations.h" void _z_declaration_clear(_z_declaration_t *decl) { switch (decl->_tag) { case _Z_DECL_KEXPR: { _z_wireexpr_clear(&decl->_body._decl_kexpr._keyexpr); break; } case _Z_UNDECL_KEXPR: { break; } case _Z_DECL_SUBSCRIBER: { _z_wireexpr_clear(&decl->_body._decl_subscriber._keyexpr); break; } case _Z_UNDECL_SUBSCRIBER: { _z_wireexpr_clear(&decl->_body._undecl_subscriber._ext_keyexpr); break; } case _Z_DECL_QUERYABLE: { _z_wireexpr_clear(&decl->_body._decl_queryable._keyexpr); break; } case _Z_UNDECL_QUERYABLE: { _z_wireexpr_clear(&decl->_body._undecl_queryable._ext_keyexpr); break; } case _Z_DECL_TOKEN: { _z_wireexpr_clear(&decl->_body._decl_token._keyexpr); break; } case _Z_UNDECL_TOKEN: { _z_wireexpr_clear(&decl->_body._undecl_token._ext_keyexpr); break; } default: case _Z_DECL_FINAL: { break; } } } _z_declaration_t _z_make_decl_keyexpr(uint16_t id, _Z_MOVE(_z_wireexpr_t) key) { return (_z_declaration_t){._tag = _Z_DECL_KEXPR, ._body = {._decl_kexpr = {._id = id, ._keyexpr = _z_wireexpr_steal(key)}}}; } _z_declaration_t _z_make_undecl_keyexpr(uint16_t id) { return (_z_declaration_t){._tag = _Z_UNDECL_KEXPR, ._body = {._undecl_kexpr = {._id = id}}}; } _z_declaration_t _z_make_decl_subscriber(_Z_MOVE(_z_wireexpr_t) key, uint32_t id) { return (_z_declaration_t){._tag = _Z_DECL_SUBSCRIBER, ._body = {._decl_subscriber = {._id = id, ._keyexpr = _z_wireexpr_steal(key)}}}; } _z_declaration_t _z_make_undecl_subscriber(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key) { return (_z_declaration_t){ ._tag = _Z_UNDECL_SUBSCRIBER, ._body = {._undecl_subscriber = {._id = id, ._ext_keyexpr = (key == NULL) ? _z_wireexpr_null() : _z_wireexpr_steal(key)}}}; } _z_declaration_t _z_make_decl_queryable(_Z_MOVE(_z_wireexpr_t) key, uint32_t id, bool complete, uint16_t distance) { return (_z_declaration_t){ ._tag = _Z_DECL_QUERYABLE, ._body = {._decl_queryable = {._id = id, ._keyexpr = _z_wireexpr_steal(key), ._ext_queryable_info = {._complete = complete, ._distance = distance}}}}; } _z_declaration_t _z_make_undecl_queryable(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key) { return (_z_declaration_t){ ._tag = _Z_UNDECL_QUERYABLE, ._body = {._undecl_queryable = {._id = id, ._ext_keyexpr = (key == NULL) ? _z_wireexpr_null() : _z_wireexpr_steal(key)}}}; } _z_declaration_t _z_make_decl_token(_Z_MOVE(_z_wireexpr_t) key, uint32_t id) { return (_z_declaration_t){._tag = _Z_DECL_TOKEN, ._body = {._decl_token = { ._id = id, ._keyexpr = _z_wireexpr_steal(key), }}}; } _z_declaration_t _z_make_undecl_token(uint32_t id, _Z_OPTIONAL _Z_MOVE(_z_wireexpr_t) key) { return (_z_declaration_t){ ._tag = _Z_UNDECL_TOKEN, ._body = { ._undecl_token = {._id = id, ._ext_keyexpr = (key == NULL) ? _z_wireexpr_null() : _z_wireexpr_steal(key)}}}; } _z_declaration_t _z_make_decl_final(void) { return (_z_declaration_t){._tag = _Z_DECL_FINAL, ._body = {._decl_final = {0}}}; } ================================================ FILE: src/protocol/definitions/interest.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/interest.h" void _z_interest_clear(_z_interest_t *interest) { _z_wireexpr_clear(&interest->_keyexpr); } _z_interest_t _z_make_interest(_Z_MOVE(_z_wireexpr_t) key, uint32_t id, uint8_t flags) { return (_z_interest_t){ ._id = id, ._keyexpr = (key == NULL) ? _z_wireexpr_null() : _z_wireexpr_steal(key), .flags = flags, }; } _z_interest_t _z_make_interest_final(uint32_t id) { return (_z_interest_t){ ._id = id, ._keyexpr = _z_wireexpr_null(), .flags = 0, }; } ================================================ FILE: src/protocol/definitions/message.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/message.h" #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" void _z_msg_reply_clear(_z_msg_reply_t *msg) { _z_push_body_clear(&msg->_body); } void _z_msg_put_clear(_z_msg_put_t *msg) { _z_bytes_drop(&msg->_payload); _z_bytes_drop(&msg->_attachment); _z_encoding_clear(&msg->_encoding); _z_timestamp_clear(&msg->_commons._timestamp); } _z_msg_query_reqexts_t _z_msg_query_required_extensions(const _z_msg_query_t *msg) { return (_z_msg_query_reqexts_t){ .body = _z_bytes_check(&msg->_ext_value.payload) || _z_encoding_check(&msg->_ext_value.encoding), .info = _z_id_check(msg->_ext_info._source_id.zid) || msg->_ext_info._source_id.eid != 0 || msg->_ext_info._source_sn != 0, .attachment = _z_bytes_check(&msg->_ext_attachment), }; } void _z_msg_query_clear(_z_msg_query_t *msg) { _z_slice_clear(&msg->_parameters); _z_bytes_drop(&msg->_ext_attachment); _z_value_clear(&msg->_ext_value); msg->_implicit_anyke = false; } void _z_msg_err_clear(_z_msg_err_t *err) { _z_encoding_clear(&err->_encoding); _z_bytes_drop(&err->_payload); } ================================================ FILE: src/protocol/definitions/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/utils/logging.h" const _z_qos_t _Z_N_QOS_DEFAULT = {._val = 5}; _z_n_msg_request_exts_t _z_n_msg_request_needed_exts(const _z_n_msg_request_t *msg) { _z_n_msg_request_exts_t ret = {.n = 0, .ext_budget = msg->_ext_budget != 0, .ext_target = msg->_ext_target != Z_QUERY_TARGET_BEST_MATCHING, .ext_qos = msg->_ext_qos._val != _Z_N_QOS_DEFAULT._val, .ext_timeout_ms = msg->_ext_timeout_ms != 0, .ext_tstamp = _z_timestamp_check(&msg->_ext_timestamp)}; if (ret.ext_budget) { ret.n += 1; } if (ret.ext_target) { ret.n += 1; } if (ret.ext_qos) { ret.n += 1; } if (ret.ext_timeout_ms) { ret.n += 1; } if (ret.ext_tstamp) { ret.n += 1; } return ret; } void _z_n_msg_request_clear(_z_n_msg_request_t *msg) { _z_wireexpr_clear(&msg->_key); switch (msg->_tag) { case _Z_REQUEST_QUERY: { _z_msg_query_clear(&msg->_body._query); } break; case _Z_REQUEST_PUT: { _z_msg_put_clear(&msg->_body._put); } break; case _Z_REQUEST_DEL: { _z_msg_del_clear(&msg->_body._del); } break; } } /*=============================*/ /* Network Messages */ /*=============================*/ void _z_push_body_clear(_z_push_body_t *msg) { if (msg->_is_put) { _z_msg_put_clear(&msg->_body._put); } } _z_push_body_t _z_push_body_steal(_z_push_body_t *msg) { _z_push_body_t ret = *msg; *msg = _z_push_body_null(); return ret; } static z_result_t _z_push_body_copy(_z_push_body_t *dst, const _z_push_body_t *src) { if (src->_is_put) { _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._put._attachment, &src->_body._put._attachment)); _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._put._payload, &src->_body._put._payload)); } else { _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._del._attachment, &src->_body._del._attachment)); } return _Z_RES_OK; } void _z_n_msg_response_final_clear(_z_n_msg_response_final_t *msg) { (void)(msg); } void _z_n_msg_push_clear(_z_n_msg_push_t *msg) { _z_wireexpr_clear(&msg->_key); _z_push_body_clear(&msg->_body); } void _z_n_msg_response_clear(_z_n_msg_response_t *msg) { _z_timestamp_clear(&msg->_ext_timestamp); _z_wireexpr_clear(&msg->_key); switch (msg->_tag) { case _Z_RESPONSE_BODY_REPLY: { _z_msg_reply_clear(&msg->_body._reply); break; } case _Z_RESPONSE_BODY_ERR: { _z_msg_err_clear(&msg->_body._err); break; } } } void _z_n_msg_oam_clear(_z_n_msg_oam_t *oam) { _z_timestamp_clear(&oam->_ext_timestamp); switch (oam->_enc) { case _Z_OAM_BODY_UNIT: { _z_msg_ext_clear_unit(&oam->_body._unit); break; } case _Z_OAM_BODY_ZINT: { _z_msg_ext_clear_zint(&oam->_body._zint); break; } case _Z_OAM_BODY_ZBUF: { _z_msg_ext_clear_zbuf(&oam->_body._zbuf); break; } } } void _z_n_msg_clear(_z_network_message_t *msg) { switch (msg->_tag) { case _Z_N_PUSH: _z_n_msg_push_clear(&msg->_body._push); break; case _Z_N_REQUEST: _z_n_msg_request_clear(&msg->_body._request); break; case _Z_N_RESPONSE: _z_n_msg_response_clear(&msg->_body._response); break; case _Z_N_RESPONSE_FINAL: _z_n_msg_response_final_clear(&msg->_body._response_final); break; case _Z_N_DECLARE: _z_n_msg_declare_clear(&msg->_body._declare); break; case _Z_N_INTEREST: _z_n_msg_interest_clear(&msg->_body._interest); break; case _Z_N_OAM: _z_n_msg_oam_clear(&msg->_body._oam); break; default: break; } } void _z_n_msg_free(_z_network_message_t **msg) { _z_network_message_t *ptr = *msg; if (ptr != NULL) { _z_n_msg_clear(ptr); z_free(ptr); *msg = NULL; } } void _z_n_msg_make_response_final(_z_network_message_t *msg, _z_zint_t rid) { msg->_tag = _Z_N_RESPONSE_FINAL; msg->_reliability = Z_RELIABILITY_DEFAULT; msg->_body._response_final._request_id = rid; } void _z_n_msg_make_declare(_z_network_message_t *msg, _z_declaration_t declaration, _z_optional_id_t interest_id) { msg->_tag = _Z_N_DECLARE; msg->_reliability = Z_RELIABILITY_DEFAULT; msg->_body._declare._interest_id = interest_id; msg->_body._declare._decl = declaration; msg->_body._declare._ext_qos = _Z_N_QOS_DEFAULT; msg->_body._declare._ext_timestamp = _z_timestamp_null(); } void _z_n_msg_make_query(_z_zenoh_message_t *msg, const _z_wireexpr_t *key, const _z_slice_t *parameters, _z_zint_t qid, z_reliability_t reliability, z_consolidation_mode_t consolidation, const _z_bytes_t *payload, const _z_encoding_t *encoding, uint64_t timeout_ms, const _z_bytes_t *attachment, _z_n_qos_t qos, const _z_source_info_t *source_info, bool implicit_anyke) { msg->_tag = _Z_N_REQUEST; msg->_reliability = reliability; msg->_body._request._tag = _Z_REQUEST_QUERY; msg->_body._request._rid = qid; msg->_body._request._key = *key; msg->_body._request._body._query._parameters = *parameters; msg->_body._request._body._query._implicit_anyke = implicit_anyke; msg->_body._request._body._query._consolidation = consolidation; msg->_body._request._body._query._ext_value.payload = (payload == NULL) ? _z_bytes_null() : *payload; msg->_body._request._body._query._ext_value.encoding = (encoding == NULL) ? _z_encoding_null() : *encoding; msg->_body._request._body._query._ext_info = (source_info == NULL) ? _z_source_info_null() : *source_info; msg->_body._request._body._query._ext_attachment = (attachment == NULL) ? _z_bytes_null() : *attachment; msg->_body._request._ext_budget = 0; msg->_body._request._ext_qos = qos; msg->_body._request._ext_target = Z_QUERY_TARGET_BEST_MATCHING; msg->_body._request._ext_timeout_ms = timeout_ms; msg->_body._request._ext_timestamp = _z_timestamp_null(); } void _z_n_msg_make_push_put(_z_network_message_t *dst, const _z_wireexpr_t *key, const _z_bytes_t *payload, const _z_encoding_t *encoding, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info) { dst->_tag = _Z_N_PUSH; dst->_reliability = reliability; dst->_body._push._key = *key; dst->_body._push._qos = qos; dst->_body._push._timestamp = _z_timestamp_null(); dst->_body._push._body._is_put = true; dst->_body._push._body._body._put._commons._timestamp = (timestamp == NULL) ? _z_timestamp_null() : *timestamp; dst->_body._push._body._body._put._commons._source_info = (source_info == NULL) ? _z_source_info_null() : *source_info; dst->_body._push._body._body._put._payload = (payload == NULL) ? _z_bytes_null() : *payload; dst->_body._push._body._body._put._encoding = (encoding == NULL) ? _z_encoding_null() : *encoding; dst->_body._push._body._body._put._attachment = (attachment == NULL) ? _z_bytes_null() : *attachment; } void _z_n_msg_make_push_del(_z_network_message_t *dst, const _z_wireexpr_t *key, _z_n_qos_t qos, const _z_timestamp_t *timestamp, z_reliability_t reliability, const _z_source_info_t *source_info) { dst->_tag = _Z_N_PUSH; dst->_reliability = reliability; dst->_body._push._key = *key; dst->_body._push._qos = qos; dst->_body._push._timestamp = _z_timestamp_null(); dst->_body._push._body._is_put = false; dst->_body._push._body._body._del._commons._timestamp = (timestamp == NULL) ? _z_timestamp_null() : *timestamp; dst->_body._push._body._body._del._commons._source_info = (source_info == NULL) ? _z_source_info_null() : *source_info; dst->_body._push._body._body._del._attachment = _z_bytes_null(); } void _z_n_msg_make_reply_ok_put(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, const _z_wireexpr_t *key, z_reliability_t reliability, z_consolidation_mode_t consolidation, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_source_info_t *source_info, const _z_bytes_t *payload, const _z_encoding_t *encoding, const _z_bytes_t *attachment) { dst->_tag = _Z_N_RESPONSE; dst->_reliability = reliability; dst->_body._response._tag = _Z_RESPONSE_BODY_REPLY; dst->_body._response._request_id = rid; dst->_body._response._key = *key; dst->_body._response._body._reply._consolidation = consolidation; dst->_body._response._body._reply._body._is_put = true; dst->_body._response._body._reply._body._body._put._commons._timestamp = (timestamp == NULL) ? _z_timestamp_null() : *timestamp; dst->_body._response._body._reply._body._body._put._commons._source_info = (source_info == NULL) ? _z_source_info_null() : *source_info; dst->_body._response._body._reply._body._body._put._payload = (payload == NULL) ? _z_bytes_null() : *payload; dst->_body._response._body._reply._body._body._put._encoding = (encoding == NULL) ? _z_encoding_null() : *encoding; dst->_body._response._body._reply._body._body._put._attachment = (attachment == NULL) ? _z_bytes_null() : *attachment; dst->_body._response._ext_qos = qos; dst->_body._response._ext_timestamp = _z_timestamp_null(); dst->_body._response._ext_responder._eid = 0; dst->_body._response._ext_responder._zid = *zid; } void _z_n_msg_make_reply_ok_del(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, const _z_wireexpr_t *key, z_reliability_t reliability, z_consolidation_mode_t consolidation, _z_n_qos_t qos, const _z_timestamp_t *timestamp, const _z_source_info_t *source_info, const _z_bytes_t *attachment) { dst->_tag = _Z_N_RESPONSE; dst->_reliability = reliability; dst->_body._response._tag = _Z_RESPONSE_BODY_REPLY; dst->_body._response._request_id = rid; dst->_body._response._key = *key; dst->_body._response._body._reply._consolidation = consolidation; dst->_body._response._body._reply._body._is_put = false; dst->_body._response._body._reply._body._body._del._commons._timestamp = (timestamp == NULL) ? _z_timestamp_null() : *timestamp; dst->_body._response._body._reply._body._body._del._commons._source_info = (source_info == NULL) ? _z_source_info_null() : *source_info; dst->_body._response._body._reply._body._body._del._attachment = (attachment == NULL) ? _z_bytes_null() : *attachment; dst->_body._response._ext_timestamp = _z_timestamp_null(); dst->_body._response._ext_qos = qos; dst->_body._response._ext_responder._eid = 0; dst->_body._response._ext_responder._zid = *zid; } void _z_n_msg_make_reply_err(_z_network_message_t *dst, const _z_id_t *zid, _z_zint_t rid, z_reliability_t reliability, _z_n_qos_t qos, const _z_bytes_t *payload, const _z_encoding_t *encoding, const _z_source_info_t *source_info) { dst->_tag = _Z_N_RESPONSE; dst->_reliability = reliability; dst->_body._response._tag = _Z_RESPONSE_BODY_ERR; dst->_body._response._request_id = rid; dst->_body._response._key = _z_wireexpr_null(); dst->_body._response._body._err._payload = (payload == NULL) ? _z_bytes_null() : *payload; dst->_body._response._body._err._encoding = (encoding == NULL) ? _z_encoding_null() : *encoding; dst->_body._response._body._err._ext_source_info = (source_info == NULL) ? _z_source_info_null() : *source_info; dst->_body._response._ext_timestamp = _z_timestamp_null(); dst->_body._response._ext_qos = qos; dst->_body._response._ext_responder._eid = 0; dst->_body._response._ext_responder._zid = *zid; } void _z_n_msg_make_interest(_z_network_message_t *msg, _z_interest_t interest) { msg->_tag = _Z_N_INTEREST; msg->_reliability = Z_RELIABILITY_DEFAULT; msg->_body._interest._interest = interest; } static z_result_t _z_n_msg_push_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst->_body._push._key, &src->_body._push._key)); return _z_push_body_copy(&dst->_body._push._body, &src->_body._push._body); } static z_result_t _z_n_msg_request_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst->_body._request._key, &src->_body._request._key)); switch (src->_body._request._tag) { case _Z_REQUEST_QUERY: _Z_RETURN_IF_ERR(_z_slice_copy(&dst->_body._request._body._query._parameters, &src->_body._request._body._query._parameters)); _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._request._body._query._ext_attachment, &src->_body._request._body._query._ext_attachment)); _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._request._body._query._ext_value.payload, &src->_body._request._body._query._ext_value.payload)); break; case _Z_REQUEST_PUT: _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._request._body._put._attachment, &src->_body._request._body._put._attachment)); _Z_RETURN_IF_ERR( _z_bytes_copy(&dst->_body._request._body._put._payload, &src->_body._request._body._put._payload)); break; case _Z_REQUEST_DEL: _Z_RETURN_IF_ERR(_z_bytes_copy(&dst->_body._request._body._del._attachment, &src->_body._request._body._del._attachment)); break; } return _Z_RES_OK; } static z_result_t _z_n_msg_response_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst->_body._response._key, &src->_body._response._key)); switch (src->_body._response._tag) { case _Z_RESPONSE_BODY_REPLY: _Z_RETURN_IF_ERR( _z_push_body_copy(&dst->_body._response._body._reply._body, &src->_body._response._body._reply._body)); break; case _Z_RESPONSE_BODY_ERR: _Z_RETURN_IF_ERR( _z_bytes_copy(&dst->_body._response._body._err._payload, &src->_body._response._body._err._payload)); break; } return _Z_RES_OK; } static z_result_t _z_n_msg_response_final_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); return _Z_RES_OK; } static z_result_t _z_n_msg_declare_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); const _z_declaration_t *src_decl = &src->_body._declare._decl; _z_declaration_t *dst_decl = &dst->_body._declare._decl; switch (src_decl->_tag) { case _Z_DECL_KEXPR: { _Z_RETURN_IF_ERR( _z_wireexpr_copy(&dst_decl->_body._decl_kexpr._keyexpr, &src_decl->_body._decl_kexpr._keyexpr)); } break; case _Z_DECL_SUBSCRIBER: { _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst_decl->_body._decl_subscriber._keyexpr, &src_decl->_body._decl_subscriber._keyexpr)); } break; case _Z_UNDECL_SUBSCRIBER: { _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst_decl->_body._undecl_subscriber._ext_keyexpr, &src_decl->_body._undecl_subscriber._ext_keyexpr)); } break; case _Z_DECL_QUERYABLE: { _Z_RETURN_IF_ERR( _z_wireexpr_copy(&dst_decl->_body._decl_queryable._keyexpr, &src_decl->_body._decl_queryable._keyexpr)); } break; case _Z_UNDECL_QUERYABLE: { _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst_decl->_body._undecl_queryable._ext_keyexpr, &src_decl->_body._undecl_queryable._ext_keyexpr)); } break; case _Z_DECL_TOKEN: { _Z_RETURN_IF_ERR( _z_wireexpr_copy(&dst_decl->_body._decl_token._keyexpr, &src_decl->_body._decl_token._keyexpr)); } break; case _Z_UNDECL_TOKEN: { _Z_RETURN_IF_ERR(_z_wireexpr_copy(&dst_decl->_body._undecl_token._ext_keyexpr, &src_decl->_body._undecl_token._ext_keyexpr)); } break; default: break; } return _Z_RES_OK; } static z_result_t _z_n_msg_interest_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); _Z_RETURN_IF_ERR( _z_wireexpr_copy(&dst->_body._interest._interest._keyexpr, &src->_body._interest._interest._keyexpr)); return _Z_RES_OK; } static z_result_t _z_n_msg_oam_copy(_z_network_message_t *dst, const _z_network_message_t *src) { memcpy(dst, src, sizeof(_z_network_message_t)); switch (src->_body._oam._enc) { case _Z_OAM_BODY_ZBUF: _Z_RETURN_IF_ERR(_z_slice_copy(&dst->_body._oam._body._zbuf._val, &src->_body._oam._body._zbuf._val)); break; default: break; } return _Z_RES_OK; } z_result_t _z_n_msg_copy(_z_network_message_t *dst, const _z_network_message_t *src) { switch (src->_tag) { case _Z_N_PUSH: return _z_n_msg_push_copy(dst, src); case _Z_N_REQUEST: return _z_n_msg_request_copy(dst, src); case _Z_N_RESPONSE: return _z_n_msg_response_copy(dst, src); case _Z_N_RESPONSE_FINAL: return _z_n_msg_response_final_copy(dst, src); case _Z_N_DECLARE: return _z_n_msg_declare_copy(dst, src); case _Z_N_INTEREST: return _z_n_msg_interest_copy(dst, src); case _Z_N_OAM: return _z_n_msg_oam_copy(dst, src); default: _Z_ERROR_RETURN(_Z_ERR_ENTITY_UNKNOWN); } } _z_network_message_t *_z_n_msg_clone(const _z_network_message_t *src) { _z_network_message_t *dst = (_z_network_message_t *)z_malloc(sizeof(_z_network_message_t)); if (dst != NULL) { _z_n_msg_copy(dst, src); } return dst; } ================================================ FILE: src/protocol/definitions/transport.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/utils/logging.h" void _z_s_msg_scout_clear(_z_s_msg_scout_t *msg) { // Nothing to do _ZP_UNUSED(msg); } /*------------------ Locators Field ------------------*/ void _z_locators_clear(_z_locator_array_t *ls) { _z_locator_array_clear(ls); } void _z_s_msg_hello_clear(_z_s_msg_hello_t *msg) { _z_locators_clear(&msg->_locators); } void _z_t_msg_join_clear(_z_t_msg_join_t *msg) { // Nothing to do _ZP_UNUSED(msg); } void _z_t_msg_init_clear(_z_t_msg_init_t *msg) { _z_slice_clear(&msg->_cookie); } void _z_t_msg_open_clear(_z_t_msg_open_t *msg) { _z_slice_clear(&msg->_cookie); } void _z_t_msg_close_clear(_z_t_msg_close_t *msg) { _ZP_UNUSED(msg); } void _z_t_msg_keep_alive_clear(_z_t_msg_keep_alive_t *msg) { _ZP_UNUSED(msg); } void _z_t_msg_frame_clear(_z_t_msg_frame_t *msg) { if (msg->_payload != NULL) { _z_zbuf_reset(msg->_payload); } } void _z_t_msg_fragment_clear(_z_t_msg_fragment_t *msg) { _z_slice_clear(&msg->_payload); } void _z_t_msg_clear(_z_transport_message_t *msg) { uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_T_JOIN: { _z_t_msg_join_clear(&msg->_body._join); } break; case _Z_MID_T_INIT: { _z_t_msg_init_clear(&msg->_body._init); } break; case _Z_MID_T_OPEN: { _z_t_msg_open_clear(&msg->_body._open); } break; case _Z_MID_T_CLOSE: { _z_t_msg_close_clear(&msg->_body._close); } break; case _Z_MID_T_KEEP_ALIVE: { _z_t_msg_keep_alive_clear(&msg->_body._keep_alive); } break; case _Z_MID_T_FRAME: { _z_t_msg_frame_clear(&msg->_body._frame); } break; case _Z_MID_T_FRAGMENT: { _z_t_msg_fragment_clear(&msg->_body._fragment); } break; default: { _Z_INFO("WARNING: Trying to clear transport message with unknown ID(%d)", mid); } break; } } z_reliability_t _z_t_msg_get_reliability(_z_transport_message_t *msg) { if (_Z_HAS_FLAG(msg->_header, _Z_FLAG_T_FRAME_R)) { return Z_RELIABILITY_RELIABLE; } return Z_RELIABILITY_BEST_EFFORT; } /*------------------ Join Message ------------------*/ _z_transport_message_t _z_t_msg_make_join(z_whatami_t whatami, _z_zint_t lease, _z_id_t zid, _z_conduit_sn_list_t next_sn) { _z_transport_message_t msg; msg._header = _Z_MID_T_JOIN; msg._body._join._version = Z_PROTO_VERSION; msg._body._join._whatami = whatami; msg._body._join._lease = lease; msg._body._join._seq_num_res = Z_SN_RESOLUTION; msg._body._join._req_id_res = Z_REQ_RESOLUTION; msg._body._join._batch_size = Z_BATCH_MULTICAST_SIZE; msg._body._join._next_sn = next_sn; msg._body._join._zid = zid; #if Z_FEATURE_FRAGMENTATION == 1 msg._body._join._patch = _Z_CURRENT_PATCH; #endif if ((lease % 1000) == 0) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_JOIN_T); } if ((Z_BATCH_MULTICAST_SIZE != _Z_DEFAULT_MULTICAST_BATCH_SIZE) || (Z_SN_RESOLUTION != _Z_DEFAULT_RESOLUTION_SIZE) || (Z_REQ_RESOLUTION != _Z_DEFAULT_RESOLUTION_SIZE)) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_JOIN_S); } #if Z_FEATURE_FRAGMENTATION == 1 bool has_patch = msg._body._join._patch != _Z_NO_PATCH; #else bool has_patch = false; #endif if (next_sn._is_qos || has_patch) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_Z); } return msg; } /*------------------ Init Message ------------------*/ _z_transport_message_t _z_t_msg_make_init_syn(z_whatami_t whatami, _z_id_t zid) { _z_transport_message_t msg; msg._header = _Z_MID_T_INIT; msg._body._init._version = Z_PROTO_VERSION; msg._body._init._whatami = whatami; msg._body._init._zid = zid; msg._body._init._seq_num_res = Z_SN_RESOLUTION; msg._body._init._req_id_res = Z_REQ_RESOLUTION; msg._body._init._batch_size = Z_BATCH_UNICAST_SIZE; _z_slice_reset(&msg._body._init._cookie); #if Z_FEATURE_FRAGMENTATION == 1 msg._body._init._patch = _Z_CURRENT_PATCH; #endif if ((msg._body._init._batch_size != _Z_DEFAULT_UNICAST_BATCH_SIZE) || (msg._body._init._seq_num_res != _Z_DEFAULT_RESOLUTION_SIZE) || (msg._body._init._req_id_res != _Z_DEFAULT_RESOLUTION_SIZE)) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_INIT_S); } #if Z_FEATURE_FRAGMENTATION == 1 if (msg._body._init._patch != _Z_NO_PATCH) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_Z); } #endif return msg; } _z_transport_message_t _z_t_msg_make_init_ack(z_whatami_t whatami, _z_id_t zid, _z_slice_t cookie) { _z_transport_message_t msg; msg._header = _Z_MID_T_INIT; _Z_SET_FLAG(msg._header, _Z_FLAG_T_INIT_A); msg._body._init._version = Z_PROTO_VERSION; msg._body._init._whatami = whatami; msg._body._init._zid = zid; msg._body._init._seq_num_res = Z_SN_RESOLUTION; msg._body._init._req_id_res = Z_REQ_RESOLUTION; msg._body._init._batch_size = Z_BATCH_UNICAST_SIZE; msg._body._init._cookie = cookie; #if Z_FEATURE_FRAGMENTATION == 1 msg._body._init._patch = _Z_CURRENT_PATCH; #endif if ((msg._body._init._batch_size != _Z_DEFAULT_UNICAST_BATCH_SIZE) || (msg._body._init._seq_num_res != _Z_DEFAULT_RESOLUTION_SIZE) || (msg._body._init._req_id_res != _Z_DEFAULT_RESOLUTION_SIZE)) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_INIT_S); } #if Z_FEATURE_FRAGMENTATION == 1 if (msg._body._init._patch != _Z_NO_PATCH) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_Z); } #endif return msg; } /*------------------ Open Message ------------------*/ _z_transport_message_t _z_t_msg_make_open_syn(_z_zint_t lease, _z_zint_t initial_sn, _z_slice_t cookie) { _z_transport_message_t msg; msg._header = _Z_MID_T_OPEN; msg._body._open._lease = lease; msg._body._open._initial_sn = initial_sn; msg._body._open._cookie = cookie; if ((lease % 1000) == 0) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_OPEN_T); } return msg; } _z_transport_message_t _z_t_msg_make_open_ack(_z_zint_t lease, _z_zint_t initial_sn) { _z_transport_message_t msg; msg._header = _Z_MID_T_OPEN; _Z_SET_FLAG(msg._header, _Z_FLAG_T_OPEN_A); msg._body._open._lease = lease; msg._body._open._initial_sn = initial_sn; _z_slice_reset(&msg._body._open._cookie); if ((lease % 1000) == 0) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_OPEN_T); } return msg; } /*------------------ Close Message ------------------*/ _z_transport_message_t _z_t_msg_make_close(uint8_t reason, bool link_only) { _z_transport_message_t msg; msg._header = _Z_MID_T_CLOSE; msg._body._close._reason = reason; if (link_only == false) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_CLOSE_S); } return msg; } /*------------------ Keep Alive Message ------------------*/ _z_transport_message_t _z_t_msg_make_keep_alive(void) { _z_transport_message_t msg; msg._header = _Z_MID_T_KEEP_ALIVE; return msg; } _z_transport_message_t _z_t_msg_make_frame(_z_zint_t sn, _z_zbuf_t *payload, z_reliability_t reliability) { _z_transport_message_t msg; msg._header = _Z_MID_T_FRAME; msg._body._frame._sn = sn; if (reliability == Z_RELIABILITY_RELIABLE) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_FRAME_R); } msg._body._frame._payload = payload; return msg; } /*------------------ Frame Message ------------------*/ _z_transport_message_t _z_t_msg_make_frame_header(_z_zint_t sn, z_reliability_t reliability) { _z_transport_message_t msg; msg._header = _Z_MID_T_FRAME; msg._body._frame._sn = sn; if (reliability == Z_RELIABILITY_RELIABLE) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_FRAME_R); } msg._body._frame._payload = NULL; return msg; } /*------------------ Fragment Message ------------------*/ _z_transport_message_t _z_t_msg_make_fragment_header(_z_zint_t sn, z_reliability_t reliability, bool is_last, bool first, bool drop) { return _z_t_msg_make_fragment(sn, _z_slice_null(), reliability, is_last, first, drop); } _z_transport_message_t _z_t_msg_make_fragment(_z_zint_t sn, _z_slice_t payload, z_reliability_t reliability, bool is_last, bool first, bool drop) { _z_transport_message_t msg; msg._header = _Z_MID_T_FRAGMENT; if (is_last == false) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_FRAGMENT_M); } if (reliability == Z_RELIABILITY_RELIABLE) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_FRAGMENT_R); } msg._body._fragment._sn = sn; msg._body._fragment._payload = payload; if (first || drop) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_Z); } msg._body._fragment.first = first; msg._body._fragment.drop = drop; return msg; } void _z_t_msg_copy_fragment(_z_t_msg_fragment_t *clone, _z_t_msg_fragment_t *msg) { clone->_payload = msg->_payload; _z_slice_copy(&clone->_payload, &msg->_payload); clone->first = msg->first; clone->drop = msg->drop; } void _z_t_msg_copy_join(_z_t_msg_join_t *clone, _z_t_msg_join_t *msg) { clone->_version = msg->_version; clone->_whatami = msg->_whatami; clone->_lease = msg->_lease; clone->_seq_num_res = msg->_seq_num_res; clone->_req_id_res = msg->_req_id_res; clone->_batch_size = msg->_batch_size; clone->_next_sn = msg->_next_sn; #if Z_FEATURE_FRAGMENTATION == 1 clone->_patch = msg->_patch; #endif memcpy(clone->_zid.id, msg->_zid.id, 16); } void _z_t_msg_copy_init(_z_t_msg_init_t *clone, _z_t_msg_init_t *msg) { clone->_version = msg->_version; clone->_whatami = msg->_whatami; clone->_seq_num_res = msg->_seq_num_res; clone->_req_id_res = msg->_req_id_res; clone->_batch_size = msg->_batch_size; memcpy(clone->_zid.id, msg->_zid.id, 16); if (!_z_slice_is_empty(&msg->_cookie)) { _z_slice_copy(&clone->_cookie, &msg->_cookie); } #if Z_FEATURE_FRAGMENTATION == 1 clone->_patch = msg->_patch; #endif } void _z_t_msg_copy_open(_z_t_msg_open_t *clone, _z_t_msg_open_t *msg) { clone->_lease = msg->_lease; clone->_initial_sn = msg->_initial_sn; if (!_z_slice_is_empty(&msg->_cookie)) { _z_slice_copy(&clone->_cookie, &msg->_cookie); } } void _z_t_msg_copy_close(_z_t_msg_close_t *clone, _z_t_msg_close_t *msg) { clone->_reason = msg->_reason; } void _z_t_msg_copy_keep_alive(_z_t_msg_keep_alive_t *clone, _z_t_msg_keep_alive_t *msg) { (void)(clone); (void)(msg); } void _z_t_msg_copy_frame(_z_t_msg_frame_t *clone, _z_t_msg_frame_t *msg) { clone->_sn = msg->_sn; if ((msg->_payload != NULL) && (clone->_payload != NULL)) { _z_zbuf_copy(clone->_payload, msg->_payload); } } /*------------------ Transport Message ------------------*/ void _z_t_msg_copy(_z_transport_message_t *clone, _z_transport_message_t *msg) { clone->_header = msg->_header; uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_T_JOIN: { _z_t_msg_copy_join(&clone->_body._join, &msg->_body._join); } break; case _Z_MID_T_INIT: { _z_t_msg_copy_init(&clone->_body._init, &msg->_body._init); } break; case _Z_MID_T_OPEN: { _z_t_msg_copy_open(&clone->_body._open, &msg->_body._open); } break; case _Z_MID_T_CLOSE: { _z_t_msg_copy_close(&clone->_body._close, &msg->_body._close); } break; case _Z_MID_T_KEEP_ALIVE: { _z_t_msg_copy_keep_alive(&clone->_body._keep_alive, &msg->_body._keep_alive); } break; case _Z_MID_T_FRAME: { _z_t_msg_copy_frame(&clone->_body._frame, &msg->_body._frame); } break; case _Z_MID_T_FRAGMENT: { _z_t_msg_copy_fragment(&clone->_body._fragment, &msg->_body._fragment); } break; default: { _Z_INFO("WARNING: Trying to copy transport message with unknown ID(%d)", mid); } break; } } void _z_s_msg_clear(_z_scouting_message_t *msg) { uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_SCOUT: { _z_s_msg_scout_clear(&msg->_body._scout); } break; case _Z_MID_HELLO: { _z_s_msg_hello_clear(&msg->_body._hello); } break; default: { _Z_INFO("WARNING: Trying to clear session message with unknown ID(%d)", mid); } break; } } /*=============================*/ /* Transport Messages */ /*=============================*/ /*------------------ Scout Message ------------------*/ _z_scouting_message_t _z_s_msg_make_scout(z_what_t what, _z_id_t zid) { _z_scouting_message_t msg; msg._header = _Z_MID_SCOUT; msg._body._scout._version = Z_PROTO_VERSION; msg._body._scout._what = what; msg._body._scout._zid = zid; return msg; } /*------------------ Hello Message ------------------*/ _z_scouting_message_t _z_s_msg_make_hello(z_whatami_t whatami, _z_id_t zid, _z_locator_array_t locators) { _z_scouting_message_t msg; msg._header = _Z_MID_HELLO; msg._body._hello._version = Z_PROTO_VERSION; msg._body._hello._whatami = whatami; msg._body._hello._zid = zid; msg._body._hello._locators = locators; if (_z_locator_array_is_empty(&locators) == false) { _Z_SET_FLAG(msg._header, _Z_FLAG_T_HELLO_L); } return msg; } void _z_s_msg_copy_scout(_z_s_msg_scout_t *clone, _z_s_msg_scout_t *msg) { clone->_what = msg->_what; clone->_version = msg->_version; memcpy(clone->_zid.id, msg->_zid.id, 16); } void _z_s_msg_copy_hello(_z_s_msg_hello_t *clone, _z_s_msg_hello_t *msg) { _z_locator_array_copy(&clone->_locators, &msg->_locators); memcpy(clone->_zid.id, msg->_zid.id, 16); clone->_whatami = msg->_whatami; } /*------------------ Scouting Message ------------------*/ void _z_s_msg_copy(_z_scouting_message_t *clone, _z_scouting_message_t *msg) { clone->_header = msg->_header; uint8_t mid = _Z_MID(msg->_header); switch (mid) { case _Z_MID_SCOUT: { _z_s_msg_copy_scout(&clone->_body._scout, &msg->_body._scout); } break; case _Z_MID_HELLO: { _z_s_msg_copy_hello(&clone->_body._hello, &msg->_body._hello); } break; default: { _Z_INFO("WARNING: Trying to copy session message with unknown ID(%d)", mid); } break; } } ================================================ FILE: src/protocol/ext.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/ext.h" #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/utils/logging.h" _z_msg_ext_t _z_msg_ext_make_unit(uint8_t id) { _z_msg_ext_t ext; ext._header = id & _Z_EXT_ID_MASK; ext._header |= _Z_MSG_EXT_ENC_UNIT; return ext; } void _z_msg_ext_clear_unit(_z_msg_ext_unit_t *ext) { (void)(ext); } void _z_msg_ext_copy_unit(_z_msg_ext_unit_t *clone, const _z_msg_ext_unit_t *ext) { (void)(clone); (void)(ext); } _z_msg_ext_t _z_msg_ext_make_zint(uint8_t id, _z_zint_t zid) { _z_msg_ext_t ext; ext._header = id & _Z_EXT_ID_MASK; ext._header |= _Z_MSG_EXT_ENC_ZINT; ext._body._zint._val = zid; return ext; } void _z_msg_ext_clear_zint(_z_msg_ext_zint_t *ext) { (void)(ext); } void _z_msg_ext_copy_zint(_z_msg_ext_zint_t *clone, const _z_msg_ext_zint_t *ext) { clone->_val = ext->_val; } _z_msg_ext_t _z_msg_ext_make_zbuf(uint8_t id, _z_slice_t zbuf) { _z_msg_ext_t ext; ext._header = id & _Z_EXT_ID_MASK; ext._header |= _Z_MSG_EXT_ENC_ZBUF; ext._body._zbuf._val = _z_slice_steal(&zbuf); return ext; } void _z_msg_ext_clear_zbuf(_z_msg_ext_zbuf_t *ext) { _z_slice_clear(&ext->_val); } void _z_msg_ext_copy_zbuf(_z_msg_ext_zbuf_t *clone, const _z_msg_ext_zbuf_t *ext) { _z_slice_copy(&clone->_val, &ext->_val); } void _z_msg_ext_copy(_z_msg_ext_t *clone, const _z_msg_ext_t *ext) { clone->_header = ext->_header; uint8_t enc = _Z_EXT_ENC(clone->_header); switch (enc) { case _Z_MSG_EXT_ENC_UNIT: { _z_msg_ext_copy_unit(&clone->_body._unit, &ext->_body._unit); } break; case _Z_MSG_EXT_ENC_ZINT: { _z_msg_ext_copy_zint(&clone->_body._zint, &ext->_body._zint); } break; case _Z_MSG_EXT_ENC_ZBUF: { _z_msg_ext_copy_zbuf(&clone->_body._zbuf, &ext->_body._zbuf); } break; default: { _Z_INFO("WARNING: Trying to copy message extension with unknown encoding(%d)", enc); } break; } } void _z_msg_ext_clear(_z_msg_ext_t *ext) { uint8_t enc = _Z_EXT_ENC(ext->_header); switch (enc) { case _Z_MSG_EXT_ENC_UNIT: { _z_msg_ext_clear_unit(&ext->_body._unit); } break; case _Z_MSG_EXT_ENC_ZINT: { _z_msg_ext_clear_zint(&ext->_body._zint); } break; case _Z_MSG_EXT_ENC_ZBUF: { _z_msg_ext_clear_zbuf(&ext->_body._zbuf); } break; default: { _Z_INFO("WARNING: Trying to free message extension with unknown encoding(%d)", enc); } break; } } ================================================ FILE: src/protocol/iobuf.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/protocol/iobuf.h" #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/result.h" /*------------------ IOSli ------------------*/ _z_iosli_t _z_iosli_wrap(const uint8_t *buf, size_t length, size_t r_pos, size_t w_pos) { assert((r_pos <= w_pos) && (w_pos <= length)); _z_iosli_t ios; ios._r_pos = r_pos; ios._w_pos = w_pos; ios._capacity = length; ios._is_alloc = false; ios._buf = (uint8_t *)buf; return ios; } _z_iosli_t _z_iosli_steal(_z_iosli_t *ios) { _z_iosli_t new_ios = *ios; *ios = _z_iosli_null(); return new_ios; } void __z_iosli_init(_z_iosli_t *ios, size_t capacity) { ios->_r_pos = 0; ios->_w_pos = 0; ios->_capacity = capacity; ios->_is_alloc = true; ios->_buf = (uint8_t *)z_malloc(capacity); if (ios->_buf == NULL) { ios->_capacity = 0; ios->_is_alloc = false; } } _z_iosli_t _z_iosli_make(size_t capacity) { _z_iosli_t ios; __z_iosli_init(&ios, capacity); return ios; } _z_iosli_t *_z_iosli_new(size_t capacity) { _z_iosli_t *pios = (_z_iosli_t *)z_malloc(sizeof(_z_iosli_t)); if (pios != NULL) { __z_iosli_init(pios, capacity); } return pios; } _z_slice_t _z_iosli_to_bytes(const _z_iosli_t *ios) { _z_slice_t a; a.len = _z_iosli_readable(ios); a.start = _z_cptr_u8_offset(ios->_buf, (ptrdiff_t)ios->_r_pos); return a; } void _z_iosli_clear(_z_iosli_t *ios) { if ((ios->_is_alloc) && (ios->_buf != NULL)) { z_free(ios->_buf); ios->_buf = NULL; } } void _z_iosli_free(_z_iosli_t **ios) { _z_iosli_t *ptr = *ios; if (ptr != NULL) { _z_iosli_clear(ptr); z_free(ptr); *ios = NULL; } } void _z_iosli_copy(_z_iosli_t *dst, const _z_iosli_t *src) { dst->_r_pos = src->_r_pos; dst->_w_pos = src->_w_pos; dst->_capacity = src->_capacity; dst->_is_alloc = src->_is_alloc; if (dst->_is_alloc) { dst->_buf = (uint8_t *)z_malloc(src->_capacity); if (dst->_buf != NULL) { (void)memcpy(dst->_buf, src->_buf, src->_capacity); } } else { dst->_buf = src->_buf; } } _z_iosli_t *_z_iosli_clone(const _z_iosli_t *src) { _z_iosli_t *dst = (_z_iosli_t *)z_malloc(_z_iosli_size(src)); if (dst != NULL) { _z_iosli_copy(dst, src); } return dst; } /*------------------ ZBuf ------------------*/ _z_zbuf_t _z_zbuf_make(size_t capacity) { _z_zbuf_t zbf = _z_zbuf_null(); zbf._ios = _z_iosli_make(capacity); if (_z_zbuf_capacity(&zbf) == 0) { return zbf; } _z_slice_t s = _z_slice_from_buf_custom_deleter(zbf._ios._buf, zbf._ios._capacity, _z_delete_context_default()); zbf._slice = _z_slice_simple_rc_new_from_val(&s); if (_z_slice_simple_rc_is_null(&zbf._slice)) { _Z_ERROR("slice rc creation failed"); _z_iosli_clear(&zbf._ios); } zbf._ios._is_alloc = false; return zbf; } _z_zbuf_t _z_zbuf_view(_z_zbuf_t *zbf, size_t length) { assert(_z_iosli_readable(&zbf->_ios) >= length); _z_zbuf_t v; v._ios = _z_iosli_wrap(_z_zbuf_get_rptr(zbf), length, 0, length); v._slice = zbf->_slice; return v; } _z_zbuf_t _z_slice_as_zbuf(_z_slice_t slice) { return (_z_zbuf_t){ ._ios = {._buf = (uint8_t *)slice.start, // Safety: `_z_zbuf_t` is an immutable buffer ._is_alloc = false, ._capacity = slice.len, ._r_pos = 0, ._w_pos = slice.len}, ._slice = _z_slice_simple_rc_null(), }; } void _z_zbuf_copy_bytes(_z_zbuf_t *dst, const _z_zbuf_t *src) { _z_iosli_copy_bytes(&dst->_ios, &src->_ios); } void _z_zbuf_copy(_z_zbuf_t *dst, const _z_zbuf_t *src) { dst->_slice = _z_slice_simple_rc_clone(&src->_slice); _z_iosli_copy_bytes(&dst->_ios, &src->_ios); } void _z_zbuf_read_bytes(_z_zbuf_t *zbf, uint8_t *dest, size_t offset, size_t length) { _z_iosli_read_bytes(&zbf->_ios, dest, offset, length); } void _z_zbuf_clear(_z_zbuf_t *zbf) { _z_iosli_clear(&zbf->_ios); _z_slice_simple_rc_drop(&zbf->_slice); } void _z_zbuf_compact(_z_zbuf_t *zbf) { if ((zbf->_ios._r_pos != 0) || (zbf->_ios._w_pos != 0)) { size_t len = _z_iosli_readable(&zbf->_ios); (void)memmove(zbf->_ios._buf, _z_zbuf_get_rptr(zbf), len); _z_zbuf_set_rpos(zbf, 0); _z_zbuf_set_wpos(zbf, len); } } void _z_zbuf_free(_z_zbuf_t **zbf) { _z_zbuf_t *ptr = *zbf; if (ptr != NULL) { _z_iosli_clear(&ptr->_ios); z_free(ptr); *zbf = NULL; } } /*------------------ WBuf ------------------*/ static z_result_t _z_wbuf_add_iosli(_z_wbuf_t *wbf, _z_iosli_t *ios) { z_result_t res = _z_iosli_svec_append(&wbf->_ioss, ios, false); if (res == _Z_RES_OK) { wbf->_w_idx++; } return res; } size_t _z_wbuf_len_iosli(const _z_wbuf_t *wbf) { return _z_iosli_svec_len(&wbf->_ioss); } _z_wbuf_t _z_wbuf_make(size_t capacity, bool is_expandable) { _z_wbuf_t wbf; if (is_expandable) { wbf._ioss = _z_iosli_svec_make(5); // Dfrag buffer layout: misc, attachment, misc, payload, misc wbf._expansion_step = capacity; } else { wbf._ioss = _z_iosli_svec_make(1); wbf._expansion_step = 0; } _z_iosli_t ios = _z_iosli_make(capacity); z_result_t res = _z_iosli_svec_append(&wbf._ioss, &ios, false); if (res != _Z_RES_OK) { _z_iosli_clear(&ios); } wbf._w_idx = 0; wbf._r_idx = 0; return wbf; } size_t _z_wbuf_capacity(const _z_wbuf_t *wbf) { size_t cap = 0; for (size_t i = 0; i < _z_wbuf_len_iosli(wbf); i++) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); cap = cap + ios->_capacity; } return cap; } size_t _z_wbuf_len(const _z_wbuf_t *wbf) { size_t len = 0; for (size_t i = wbf->_r_idx; (i < _z_wbuf_len_iosli(wbf)) && (i <= wbf->_w_idx); i++) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); len = len + _z_iosli_readable(ios); } return len; } size_t _z_wbuf_space_left(const _z_wbuf_t *wbf) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); return _z_iosli_writable(ios); } uint8_t _z_wbuf_read(_z_wbuf_t *wbf) { uint8_t ret = 0; do { assert(wbf->_r_idx <= wbf->_w_idx); _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_r_idx); size_t readable = _z_iosli_readable(ios); if (readable > (size_t)0) { ret = _z_iosli_read(ios); break; } else { wbf->_r_idx = wbf->_r_idx + (size_t)1; } } while (1); return ret; } void _z_wbuf_read_bytes(_z_wbuf_t *wbf, uint8_t *dest, size_t offset, size_t length) { size_t loffset = offset; size_t llength = length; do { assert(wbf->_r_idx <= wbf->_w_idx); _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_r_idx); size_t readable = _z_iosli_readable(ios); if (readable > (size_t)0) { size_t to_read = (readable <= llength) ? readable : llength; _z_iosli_read_bytes(ios, dest, loffset, to_read); loffset = loffset + to_read; llength = llength - to_read; } else { wbf->_r_idx = wbf->_r_idx + 1; } } while (llength > (size_t)0); } uint8_t _z_wbuf_get(const _z_wbuf_t *wbf, size_t pos) { size_t current = pos; size_t i = 0; _z_iosli_t *ios; do { assert(i < _z_iosli_svec_len(&wbf->_ioss)); ios = _z_wbuf_get_iosli(wbf, i); if (current < ios->_capacity) { break; } else { current = current - ios->_capacity; } i = i + (size_t)1; } while (1); return _z_iosli_get(ios, current); } z_result_t _z_wbuf_write(_z_wbuf_t *wbf, uint8_t b) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); if (!_z_iosli_can_write(ios)) { // Check if we need to allocate new buffer if (wbf->_ioss._len <= wbf->_w_idx + 1) { if (wbf->_expansion_step == 0) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NO_SPACE); } _z_iosli_t tmp = _z_iosli_make(wbf->_expansion_step); _Z_CLEAN_RETURN_IF_ERR(_z_wbuf_add_iosli(wbf, &tmp), _z_iosli_clear(&tmp)); } else { wbf->_w_idx++; } ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); } _z_iosli_write(ios, b); return _Z_RES_OK; } z_result_t _z_wbuf_write_bytes(_z_wbuf_t *wbf, const uint8_t *bs, size_t offset, size_t length) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); size_t writable = _z_iosli_writable(ios); if (writable >= length) { _z_iosli_write_bytes(ios, bs, offset, length); } else if (wbf->_expansion_step != 0) { // Expand buffer to write all the data size_t llength = length; size_t loffset = offset; _z_iosli_write_bytes(ios, bs, loffset, writable); llength -= writable; loffset += writable; while (llength > (size_t)0) { _z_iosli_t tmp = _z_iosli_make(wbf->_expansion_step); _Z_CLEAN_RETURN_IF_ERR(_z_wbuf_add_iosli(wbf, &tmp), _z_iosli_clear(&tmp)); ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); writable = _z_iosli_writable(ios); if (llength < writable) { writable = llength; } _z_iosli_write_bytes(ios, bs, loffset, writable); llength -= writable; loffset += writable; } } else { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NO_SPACE); } return _Z_RES_OK; } z_result_t _z_wbuf_wrap_bytes(_z_wbuf_t *wbf, const uint8_t *bs, size_t offset, size_t length) { z_result_t ret = _Z_RES_OK; _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); size_t curr_space = _z_iosli_writable(ios); // Block writing on this ioslice ios->_capacity = ios->_w_pos; // Wrap data _z_iosli_t wios = _z_iosli_wrap(bs, length, offset, offset + length); _Z_CLEAN_RETURN_IF_ERR(_z_wbuf_add_iosli(wbf, &wios), _z_iosli_clear(&wios)); // If svec expanded, ios is invalid, refresh pointer. ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx - 1); // Set remaining space as a new ioslice wios = _z_iosli_wrap(_z_ptr_u8_offset(ios->_buf, (ptrdiff_t)ios->_w_pos), curr_space, 0, 0); _Z_CLEAN_RETURN_IF_ERR(_z_wbuf_add_iosli(wbf, &wios), _z_iosli_clear(&wios)); return ret; } void _z_wbuf_put(_z_wbuf_t *wbf, uint8_t b, size_t pos) { size_t current = pos; size_t i = 0; _z_iosli_t *ios; do { assert(i < _z_iosli_svec_len(&wbf->_ioss)); ios = _z_wbuf_get_iosli(wbf, i); if (current < ios->_capacity) { break; } else { current = current - ios->_capacity; } i = i + (size_t)1; } while (1); _z_iosli_put(ios, b, current); } size_t _z_wbuf_get_rpos(const _z_wbuf_t *wbf) { size_t pos = 0; _z_iosli_t *ios; for (size_t i = 0; i < wbf->_r_idx; i++) { ios = _z_wbuf_get_iosli(wbf, i); pos = pos + ios->_capacity; } ios = _z_wbuf_get_iosli(wbf, wbf->_r_idx); pos = pos + ios->_r_pos; return pos; } size_t _z_wbuf_get_wpos(const _z_wbuf_t *wbf) { size_t pos = 0; _z_iosli_t *ios; for (size_t i = 0; i < wbf->_w_idx; i++) { ios = _z_wbuf_get_iosli(wbf, i); pos = pos + ios->_capacity; } ios = _z_wbuf_get_iosli(wbf, wbf->_w_idx); pos = pos + ios->_w_pos; return pos; } void _z_wbuf_set_rpos(_z_wbuf_t *wbf, size_t pos) { size_t current = pos; size_t i = 0; do { assert(i <= wbf->_w_idx); _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); if (current <= ios->_w_pos) { wbf->_r_idx = i; ios->_r_pos = current; break; } ios->_r_pos = ios->_w_pos; current = current - ios->_capacity; i = i + (size_t)1; } while (1); } void _z_wbuf_set_wpos(_z_wbuf_t *wbf, size_t pos) { size_t current = pos; size_t i = 0; do { assert(i <= _z_iosli_svec_len(&wbf->_ioss)); _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); if ((current <= ios->_capacity) && (current >= ios->_r_pos)) { wbf->_w_idx = i; ios->_w_pos = current; break; } ios->_w_pos = ios->_capacity; current = current - ios->_capacity; i = i + (size_t)1; } while (1); } _z_zbuf_t _z_wbuf_to_zbuf(const _z_wbuf_t *wbf) { size_t len = _z_wbuf_len(wbf); _z_zbuf_t zbf = _z_zbuf_make(len); for (size_t i = wbf->_r_idx; i <= wbf->_w_idx; i++) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); _z_iosli_write_bytes(&zbf._ios, ios->_buf, ios->_r_pos, _z_iosli_readable(ios)); } return zbf; } _z_zbuf_t _z_wbuf_moved_as_zbuf(_z_wbuf_t *wbf) { // Can only move single buffer wbuf assert(_z_iosli_svec_len(&wbf->_ioss) == 1); _z_zbuf_t zbf = _z_zbuf_null(); _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, 0); zbf._ios = _z_iosli_steal(ios); _z_slice_t s = _z_slice_from_buf_custom_deleter(zbf._ios._buf, zbf._ios._capacity, _z_delete_context_default()); zbf._slice = _z_slice_simple_rc_new_from_val(&s); if (_z_slice_simple_rc_is_null(&zbf._slice)) { _Z_ERROR("slice rc creation failed"); } zbf._ios._is_alloc = false; _z_wbuf_clear(wbf); return zbf; } z_result_t _z_wbuf_siphon(_z_wbuf_t *dst, _z_wbuf_t *src, size_t length) { z_result_t ret = _Z_RES_OK; size_t llength = length; _z_iosli_t *wios = _z_wbuf_get_iosli(dst, dst->_w_idx); size_t writable = _z_iosli_writable(wios); // Siphon does not work (as of now) on expandable dst buffers if (writable >= length) { do { assert(src->_r_idx <= src->_w_idx); _z_iosli_t *rios = _z_wbuf_get_iosli(src, src->_r_idx); size_t readable = _z_iosli_readable(rios); if (readable > (size_t)0) { size_t to_read = (readable <= llength) ? readable : llength; uint8_t *w_pos = _z_ptr_u8_offset(wios->_buf, (ptrdiff_t)wios->_w_pos); (void)memcpy(w_pos, rios->_buf + rios->_r_pos, to_read); rios->_r_pos = rios->_r_pos + to_read; llength -= to_read; wios->_w_pos += to_read; } else { src->_r_idx++; } } while (llength > (size_t)0); } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NO_SPACE); ret = _Z_ERR_TRANSPORT_NO_SPACE; } return ret; } void _z_wbuf_copy(_z_wbuf_t *dst, const _z_wbuf_t *src) { dst->_r_idx = src->_r_idx; dst->_w_idx = src->_w_idx; dst->_expansion_step = src->_expansion_step; _z_iosli_svec_copy(&dst->_ioss, &src->_ioss, false); } void _z_wbuf_reset(_z_wbuf_t *wbf) { wbf->_r_idx = 0; wbf->_w_idx = 0; // Reset to default iosli allocation for (size_t i = 0; i < _z_iosli_svec_len(&wbf->_ioss); i++) { _z_iosli_t *ios = _z_wbuf_get_iosli(wbf, i); if (!ios->_is_alloc) { _z_iosli_svec_remove(&wbf->_ioss, i, false); } else { _z_iosli_reset(ios); } } } void _z_wbuf_clear(_z_wbuf_t *wbf) { _z_iosli_svec_clear(&wbf->_ioss); *wbf = _z_wbuf_null(); } void _z_wbuf_free(_z_wbuf_t **wbf) { _z_wbuf_t *ptr = *wbf; if (ptr != NULL) { _z_wbuf_clear(ptr); z_free(ptr); *wbf = NULL; } } ================================================ FILE: src/runtime/background_executor.c ================================================ #include "zenoh-pico/runtime/background_executor.h" #if Z_FEATURE_MULTI_THREAD == 1 typedef struct _z_background_executor_inner_t { _z_executor_t _executor; _z_mutex_t _mutex; _z_condvar_t _condvar; _z_atomic_size_t _waiters; size_t _thread_idx; _z_atomic_bool_t _started; _z_atomic_size_t _thread_checkers; _z_task_t _task; } _z_background_executor_inner_t; static inline bool _is_called_from_executor(_z_background_executor_inner_t *be) { bool res = false; _z_atomic_size_fetch_add(&be->_thread_checkers, 1, _z_memory_order_acq_rel); if (_z_atomic_bool_load(&be->_started, _z_memory_order_acquire)) { // only check task id if executor is started _z_task_id_t current_task_id = _z_task_current_id(); _z_task_id_t executor_task_id = _z_task_get_id(&be->_task); res = _z_task_id_equal(¤t_task_id, &executor_task_id); } _z_atomic_size_fetch_sub(&be->_thread_checkers, 1, _z_memory_order_acq_rel); return res; } z_result_t _z_background_executor_inner_suspend_and_lock(_z_background_executor_inner_t *be, bool check_executor_thread) { if (check_executor_thread && _is_called_from_executor(be)) { return _Z_ERR_INVALID; // suspend cannot be called from executor thread } _z_atomic_size_fetch_add(&be->_waiters, 1, _z_memory_order_acq_rel); return _z_mutex_lock(&be->_mutex); } z_result_t _z_background_executor_inner_suspend(_z_background_executor_inner_t *be) { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, true)); return _z_mutex_unlock(&be->_mutex); } z_result_t _z_background_executor_inner_unlock_and_resume(_z_background_executor_inner_t *be) { _z_atomic_size_fetch_sub(&be->_waiters, 1, _z_memory_order_acq_rel); _Z_CLEAN_RETURN_IF_ERR(_z_condvar_signal_all(&be->_condvar), _z_mutex_unlock(&be->_mutex)); return _z_mutex_unlock(&be->_mutex); } z_result_t _z_background_executor_inner_resume(_z_background_executor_inner_t *be) { if (_is_called_from_executor(be)) { return _Z_ERR_INVALID; // resume cannot be called from executor thread } _Z_RETURN_IF_ERR(_z_mutex_lock(&be->_mutex)); return _z_background_executor_inner_unlock_and_resume(be); } z_result_t _z_background_executor_inner_run_forever(_z_background_executor_inner_t *be, size_t thread_idx) { _Z_RETURN_IF_ERR(_z_mutex_lock(&be->_mutex)); while (true) { while (_z_atomic_size_load(&be->_waiters, _z_memory_order_acquire) > 0) { if (thread_idx < be->_thread_idx) { // extra check to allow to stop executor thread even if there are waiters break; // stop requested, exit the loop and end the thread } // if there are waiters, sleep until they are resumed _Z_CLEAN_RETURN_IF_ERR(_z_condvar_wait(&be->_condvar, &be->_mutex), _z_mutex_unlock(&be->_mutex)); } if (thread_idx < be->_thread_idx) { break; // stop requested, exit the loop and end the thread } _z_executor_spin_result_t res = _z_executor_spin(&be->_executor); if (res.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS) { // no pending tasks, sleep until next task is added _Z_CLEAN_RETURN_IF_ERR(_z_condvar_wait(&be->_condvar, &be->_mutex), _z_mutex_unlock(&be->_mutex)); } else if (res.status == _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT) { // we have pending timed tasks but they are not // ready yet, sleep until the next one is ready z_clock_t now = z_clock_now(); if (zp_clock_elapsed_ms_since(&res.next_wake_up_time, &now) > 1) { // sleep until next task is ready z_result_t wait_result = _z_condvar_wait_until(&be->_condvar, &be->_mutex, &res.next_wake_up_time); if (wait_result != Z_ETIMEDOUT && wait_result != _Z_RES_OK) { return _z_mutex_unlock(&be->_mutex); } } } } return _z_mutex_unlock(&be->_mutex); } z_result_t _z_background_executor_inner_spawn(_z_background_executor_inner_t *be, _z_fut_t *fut, _z_fut_handle_t *handle) { z_result_t ret = _Z_RES_OK; if (_is_called_from_executor(be)) { *handle = _z_executor_spawn(&be->_executor, fut); } else { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, false)); *handle = _z_executor_spawn(&be->_executor, fut); ret = _z_background_executor_inner_unlock_and_resume(be); } return _z_fut_handle_is_null(*handle) ? _Z_ERR_SYSTEM_OUT_OF_MEMORY : ret; } z_result_t _z_background_executor_inner_get_fut_status(_z_background_executor_inner_t *be, const _z_fut_handle_t *handle, _z_fut_status_t *status_out) { if (_is_called_from_executor(be)) { *status_out = _z_executor_get_fut_status(&be->_executor, handle); return _Z_RES_OK; } else { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, false)); *status_out = _z_executor_get_fut_status(&be->_executor, handle); return _z_background_executor_inner_unlock_and_resume(be); } } z_result_t _z_background_executor_inner_cancel_fut(_z_background_executor_inner_t *be, const _z_fut_handle_t *handle) { if (_is_called_from_executor(be)) { return _z_executor_cancel_fut(&be->_executor, handle) ? _Z_RES_OK : _Z_ERR_INVALID; } else { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, false)); _z_executor_cancel_fut(&be->_executor, handle); return _z_background_executor_inner_unlock_and_resume(be); } } z_result_t _z_background_executor_inner_stop(_z_background_executor_inner_t *be) { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, true)); // executor is now suspended, so no API can be called from its thread // in addition we are holding the mutex, so no other thread can request to stop or start the executor if (!_z_atomic_bool_load(&be->_started, _z_memory_order_acquire)) { return _z_background_executor_inner_unlock_and_resume(be); } while (_z_atomic_size_load(&be->_thread_checkers, _z_memory_order_acquire) > 0) { z_sleep_us(10); } be->_thread_idx++; _z_atomic_bool_store(&be->_started, false, _z_memory_order_release); _z_task_t task_to_join = be->_task; z_result_t ret = _z_background_executor_inner_unlock_and_resume(be); // after resume the executor thread will proceed to stop directly without trying to execute any tasks _Z_SET_IF_OK(ret, _z_task_join(&task_to_join)); return ret; } void _z_background_executor_inner_clear(_z_background_executor_inner_t *be) { _z_background_executor_inner_stop(be); _z_executor_destroy(&be->_executor); _z_condvar_drop(&be->_condvar); _z_mutex_drop(&be->_mutex); } void *_z_background_executor_inner_task_fn(void *arg) { _z_background_executor_inner_t *be = (_z_background_executor_inner_t *)arg; size_t thread_idx = be->_thread_idx; // this is safe since _z_background_executor_inner_start holds the mutex until // be->_started is set to true. _z_atomic_bool_store(&be->_started, true, _z_memory_order_release); _z_background_executor_inner_run_forever(be, thread_idx); return NULL; } z_result_t _z_background_executor_inner_init_deferred(_z_background_executor_inner_t *be) { _Z_RETURN_IF_ERR(_z_mutex_init(&be->_mutex)); _Z_CLEAN_RETURN_IF_ERR(_z_condvar_init(&be->_condvar), _z_mutex_drop(&be->_mutex)); _z_executor_init(&be->_executor); _z_atomic_size_init(&be->_waiters, 0); _z_atomic_size_init(&be->_thread_checkers, 0); be->_thread_idx = 0; _z_atomic_bool_init(&be->_started, false); return _Z_RES_OK; } z_result_t _z_background_executor_inner_start(_z_background_executor_inner_t *be, z_task_attr_t *task_attr) { _Z_RETURN_IF_ERR(_z_background_executor_inner_suspend_and_lock(be, true)); if (_z_atomic_bool_load(&be->_started, _z_memory_order_acquire)) { // already started, just return return _z_background_executor_inner_unlock_and_resume(be); } z_result_t ret = _z_task_init(&be->_task, task_attr, _z_background_executor_inner_task_fn, be); if (ret == _Z_RES_OK) { while (!_z_atomic_bool_load(&be->_started, _z_memory_order_acquire)) { z_sleep_us(10); // wait until the executor thread sets the state to started } } // resume the executor thread to let it proceed to run z_result_t ret2 = _z_background_executor_inner_unlock_and_resume(be); _Z_SET_IF_OK(ret, ret2); return ret; } z_result_t _z_background_executor_init_deferred(_z_background_executor_t *be) { be->_inner = _z_background_executor_inner_rc_null(); _z_background_executor_inner_t *inner = (_z_background_executor_inner_t *)z_malloc(sizeof(_z_background_executor_inner_t)); if (!inner) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } if (_z_background_executor_inner_init_deferred(inner) != _Z_RES_OK) { z_free(inner); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } be->_inner = _z_background_executor_inner_rc_new(inner); if (_Z_RC_IS_NULL(&be->_inner)) { _z_background_executor_inner_clear(inner); z_free(inner); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return _Z_RES_OK; } z_result_t _z_background_executor_start(_z_background_executor_t *be, z_task_attr_t *task_attr) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_start(_Z_RC_IN_VAL(&be->_inner), task_attr); } z_result_t _z_background_executor_stop(_z_background_executor_t *be) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_stop(_Z_RC_IN_VAL(&be->_inner)); } z_result_t _z_background_executor_init(_z_background_executor_t *be, z_task_attr_t *task_attr) { _Z_RETURN_IF_ERR(_z_background_executor_init_deferred(be)); _Z_CLEAN_RETURN_IF_ERR(_z_background_executor_start(be, task_attr), _z_background_executor_destroy(be)); return _Z_RES_OK; } z_result_t _z_background_executor_spawn(_z_background_executor_t *be, _z_fut_t *fut, _z_fut_handle_t *handle_out) { _z_fut_handle_t dummy_handle; if (handle_out == NULL) { handle_out = &dummy_handle; } *handle_out = _z_fut_handle_null(); if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_spawn(_Z_RC_IN_VAL(&be->_inner), fut, handle_out); } z_result_t _z_background_executor_suspend(_z_background_executor_t *be) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_suspend(_Z_RC_IN_VAL(&be->_inner)); } z_result_t _z_background_executor_resume(_z_background_executor_t *be) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_resume(_Z_RC_IN_VAL(&be->_inner)); } void _z_background_executor_destroy(_z_background_executor_t *be) { _z_background_executor_inner_rc_drop(&be->_inner); } z_result_t _z_background_executor_get_fut_status(_z_background_executor_t *be, const _z_fut_handle_t *handle, _z_fut_status_t *status_out) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_get_fut_status(_Z_RC_IN_VAL(&be->_inner), handle, status_out); } z_result_t _z_background_executor_cancel_fut(_z_background_executor_t *be, const _z_fut_handle_t *handle) { if (_Z_RC_IS_NULL(&be->_inner)) { return _Z_ERR_INVALID; } return _z_background_executor_inner_cancel_fut(_Z_RC_IN_VAL(&be->_inner), handle); } z_result_t _z_background_executor_clone(_z_background_executor_t *dst, const _z_background_executor_t *src) { if (_Z_RC_IS_NULL(&src->_inner)) { dst->_inner = _z_background_executor_inner_rc_null(); return _Z_RES_OK; } dst->_inner = _z_background_executor_inner_rc_clone(&src->_inner); if (_Z_RC_IS_NULL(&dst->_inner)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } return _Z_RES_OK; } bool _z_background_executor_is_running(const _z_background_executor_t *be) { if (_Z_RC_IS_NULL(&be->_inner)) { return false; } return _z_atomic_bool_load(&_Z_RC_IN_VAL(&be->_inner)->_started, _z_memory_order_acquire); } #else // to prevent "empty compilation unit" warning when multi-threading is disabled typedef int _z_background_executor_inner_t; #endif ================================================ FILE: src/runtime/executor.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/runtime/executor.h" _z_fut_handle_t _z_executor_spawn(_z_executor_t *executor, _z_fut_t *fut) { _z_fut_data_t fut_data; _z_fut_move(&fut_data._fut, fut); fut_data._schedule = _z_fut_schedule_running(); if (executor->_next_fut_id == 0) { executor->_next_fut_id++; // Skip 0 since it's reserved for null handle } _z_fut_data_hmap_index_t idx = _z_fut_data_hmap_insert(&executor->_tasks, &executor->_next_fut_id, &fut_data); _z_fut_handle_t handle = _z_fut_handle_null(); if (!_z_fut_data_hmap_index_valid(idx)) { return handle; } handle._id = executor->_next_fut_id; executor->_next_fut_id++; // can't fail since we have enough capacity for all tasks in the hashmap _z_fut_data_hmap_index_deque_push_back(&executor->_ready_tasks, &idx); return handle; } _z_executor_spin_result_t _z_executor_get_next_fut(_z_executor_t *executor, _z_fut_data_hmap_index_t *task_idx) { _z_executor_spin_result_t result; result.status = _Z_EXECUTOR_SPIN_RESULT_NO_TASKS; _z_fut_data_hmap_index_t *sleeping_idx_ptr = _z_sleeping_fut_pqueue_peek(&executor->_sleeping_tasks); if (sleeping_idx_ptr != NULL) { z_clock_t now = z_clock_now(); uint64_t wake_up_time_ms = _z_fut_schedule_get_wake_up_time_ms( _z_fut_data_hmap_node_at(&executor->_tasks, *sleeping_idx_ptr)->val._schedule); z_clock_t wake_up_time = executor->_epoch; z_clock_advance_ms(&wake_up_time, (unsigned long)wake_up_time_ms); if (zp_clock_elapsed_ms_since(&now, &wake_up_time) > 0) { // The sleeping task is ready to execute _z_fut_data_hmap_index_t sleeping_idx; _z_sleeping_fut_pqueue_pop(&executor->_sleeping_tasks, &sleeping_idx); if (_z_fut_data_hmap_index_deque_pop_front(&executor->_ready_tasks, task_idx)) { // We have a non-sleeping task to execute, we should re-enqueue the ready sleeping task as non-sleeping // one and execute the non-sleeping task first. _z_fut_data_hmap_index_deque_push_back(&executor->_ready_tasks, &sleeping_idx); // Mark the sleeping task as ready since it's now // re-enqueued to the ready task queue and can be executed. _z_fut_data_hmap_node_at(&executor->_tasks, sleeping_idx)->val._schedule = _z_fut_schedule_ready(); } else { // No non-sleeping task, execute the ready sleeping task directly. *task_idx = sleeping_idx; } result.status = _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK; } else if (_z_fut_data_hmap_index_deque_pop_front(&executor->_ready_tasks, task_idx)) { // We have a non-sleeping task to execute result.status = _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK; } else { // No non-sleeping task, we should wait for the sleeping task to be ready. result.status = _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT; result.next_wake_up_time = wake_up_time; } } else if (_z_fut_data_hmap_index_deque_pop_front(&executor->_ready_tasks, task_idx)) { // We have a non-sleeping task to execute result.status = _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK; } return result; } _z_executor_spin_result_t _z_executor_spin(_z_executor_t *executor) { _z_fut_data_hmap_index_t fut_idx; _z_fut_data_t *fut_data = NULL; _z_executor_spin_result_t result; // Set context before spinning to make sure the sleeping task queue can access the task pool to compare the wake-up // time, in case executor was moved. _z_sleeping_fut_pqueue_set_ctx(&executor->_sleeping_tasks, &executor->_tasks); while (true) { // Loop until we find non-null task to execute result = _z_executor_get_next_fut(executor, &fut_idx); if (result.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS || result.status == _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT) { // No tasks to execute return result; } fut_data = &_z_fut_data_hmap_node_at(&executor->_tasks, fut_idx)->val; if (fut_data->_fut._fut_fn == NULL) { // idle task, just skip it and check the next task. _z_fut_data_hmap_remove_at(&executor->_tasks, fut_idx, NULL); // Remove the idle task from the task pool continue; } else if (_z_fut_schedule_get_status(fut_data->_schedule) != _Z_FUT_STATUS_SUSPENDED) { break; } } _z_fut_fn_result_t fn_result = fut_data->_fut._fut_fn(fut_data->_fut._fut_arg, executor); if (fn_result._status == _Z_FUT_STATUS_RUNNING) { // The task is still running, we should re-enqueue it to the executor. fut_data->_schedule = _z_fut_schedule_running(); // can't fail since we have enough capacity for all tasks in the hashmap _z_fut_data_hmap_index_deque_push_back(&executor->_ready_tasks, &fut_idx); } else if (fn_result._status == _Z_FUT_STATUS_SLEEPING) { // The task is sleeping, we should move it to the sleeping task queue with the wake-up time. fut_data->_schedule = _z_fut_schedule_sleeping((uint64_t)zp_clock_elapsed_ms_since(&fn_result._wake_up_time, &executor->_epoch)); // can't fail since we have enough capacity for all tasks in the hashmap _z_sleeping_fut_pqueue_push(&executor->_sleeping_tasks, &fut_idx); } else if (fn_result._status == _Z_FUT_STATUS_READY) { // The task is ready, we should destroy it to free the resource. _z_fut_data_hmap_remove_at(&executor->_tasks, fut_idx, NULL); } else if (fn_result._status == _Z_FUT_STATUS_SUSPENDED) { // The task is suspended, we should keep it in the task pool with the suspended status, and it will be skipped // in the next spin until it's resumed by external events. fut_data->_schedule = _z_fut_schedule_suspended(); } return result; } _z_fut_status_t _z_executor_get_fut_status(const _z_executor_t *executor, const _z_fut_handle_t *handle) { if (_z_fut_handle_is_null(*handle)) { return _Z_FUT_STATUS_READY; // Invalid handle is considered as ready (i.e., not running or sleeping) } _z_fut_data_t *fut_data = _z_fut_data_hmap_get((_z_fut_data_hmap_t *)&executor->_tasks, &handle->_id); if (fut_data == NULL) { return _Z_FUT_STATUS_READY; // If the task is not found in the task pool, it means it's already completed and // removed, we consider it as ready (i.e., not running or sleeping) } return _z_fut_schedule_get_status(fut_data->_schedule); } bool _z_executor_cancel_fut(_z_executor_t *executor, const _z_fut_handle_t *handle) { if (_z_fut_handle_is_null(*handle)) { return false; } _z_fut_data_t *fut = _z_fut_data_hmap_get(&executor->_tasks, &handle->_id); if (fut == NULL) { return false; } // We leave the cancelled task in the NULL state, to let executor remove it while spinning, // since we don't want to break the sleeping/ready task queue order by removing the cancelled task immediately. _z_fut_data_destroy(fut); return true; } bool _z_executor_resume_suspended_fut(_z_executor_t *executor, const _z_fut_handle_t *handle) { if (_z_fut_handle_is_null(*handle)) { return false; } _z_fut_data_hmap_index_t fut_idx = _z_fut_data_hmap_get_idx(&executor->_tasks, &handle->_id); if (!_z_fut_data_hmap_index_valid(fut_idx)) { return false; } _z_fut_data_t *fut = &_z_fut_data_hmap_node_at(&executor->_tasks, fut_idx)->val; if (_z_fut_schedule_get_status(fut->_schedule) != _Z_FUT_STATUS_SUSPENDED) { return false; } // Mark the suspended task as ready, and re-enqueue it to the ready task queue. fut->_schedule = _z_fut_schedule_ready(); // can't fail since we have enough capacity for all tasks in the hashmap _z_fut_data_hmap_index_deque_push_back(&executor->_ready_tasks, &fut_idx); return true; } ================================================ FILE: src/session/cancellation.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/cancellation.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/query.h" static inline void _z_cancellation_handlers_storage_clear(_z_cancellation_handlers_storage_t *storage) { _z_cancellation_token_on_cancel_handler_intmap_clear(&storage->_handlers); _z_sync_group_notifier_drop(&storage->_cancel_sync_notifier); } static inline void _z_cancellation_handlers_storage_remove(_z_cancellation_handlers_storage_t *storage, size_t handler_id) { _z_cancellation_token_on_cancel_handler_intmap_remove(&storage->_handlers, handler_id); } void _z_cancellation_token_clear_all_except_mutex(_z_cancellation_token_t *ct) { _z_cancellation_handlers_storage_clear(&ct->_handlers); _z_sync_group_drop(&ct->_sync_group); } z_result_t _z_cancellation_token_on_cancel_handler_call(_z_cancellation_token_on_cancel_handler_t *handler) { z_result_t ret = handler->_on_cancel != NULL ? handler->_on_cancel(handler->_arg) : _Z_RES_OK; _z_cancellation_token_on_cancel_handler_drop(handler); return ret; } static inline _z_cancellation_handlers_storage_t _z_cancellation_handlers_storage_null(void) { _z_cancellation_handlers_storage_t storage; _z_cancellation_token_on_cancel_handler_intmap_init(&storage._handlers); storage._next_handler_id = 0; storage._cancel_sync_notifier = _z_sync_group_notifier_null(); return storage; } z_result_t _z_cancellation_handlers_storage_add(_z_cancellation_handlers_storage_t *storage, _z_cancellation_token_on_cancel_handler_t *handler, size_t *out_handler_id) { _z_cancellation_token_on_cancel_handler_t *handler_on_heap = (_z_cancellation_token_on_cancel_handler_t *)z_malloc(sizeof(_z_cancellation_token_on_cancel_handler_t)); if (handler_on_heap == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _z_cancellation_token_on_cancel_handler_t *ret = _z_cancellation_token_on_cancel_handler_intmap_insert( &storage->_handlers, storage->_next_handler_id, handler_on_heap); if (ret == NULL) { z_free(handler_on_heap); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _z_cancellation_token_on_cancel_handler_move(ret, handler); *out_handler_id = storage->_next_handler_id++; return _Z_RES_OK; } void _z_cancellation_handlers_storage_cancel(_z_cancellation_handlers_storage_t *storage, z_result_t *ret) { *ret = _Z_RES_OK; _z_cancellation_token_on_cancel_handler_intmap_iterator_t it = _z_cancellation_token_on_cancel_handler_intmap_iterator_make(&storage->_handlers); while (*ret == _Z_RES_OK && _z_cancellation_token_on_cancel_handler_intmap_iterator_next(&it)) { _z_cancellation_token_on_cancel_handler_t *h = _z_cancellation_token_on_cancel_handler_intmap_iterator_value(&it); *ret = _z_cancellation_token_on_cancel_handler_call(h); } // drop sync handler in the end to ensure that all concurrent cancel calls return only once all work is done _z_cancellation_handlers_storage_clear(storage); } z_result_t _z_cancellation_token_drop_cancel_sync_notifier(void *arg) { _z_sync_group_notifier_t *n = (_z_sync_group_notifier_t *)arg; _z_sync_group_notifier_drop(n); return _Z_RES_OK; } z_result_t _z_cancellation_token_create(_z_cancellation_token_t *ct) { z_result_t ret = _Z_RES_OK; ct->_cancel_result = _Z_RES_OK; ct->_handlers = _z_cancellation_handlers_storage_null(); ret = _z_sync_group_create(&ct->_sync_group); // add notifier to synchronize cancel operation _Z_SET_IF_OK(ret, _z_sync_group_create_notifier(&ct->_sync_group, &ct->_handlers._cancel_sync_notifier)); _Z_CLEAN_RETURN_IF_ERR(ret, _z_cancellation_token_clear_all_except_mutex(ct)); #if Z_FEATURE_MULTI_THREAD == 1 _Z_CLEAN_RETURN_IF_ERR(_z_mutex_init(&ct->_mutex), _z_sync_group_drop(&ct->_sync_group); _z_cancellation_token_clear_all_except_mutex(ct)); #endif return _Z_RES_OK; } static inline z_result_t _z_cancellation_token_lock(const _z_cancellation_token_t *ct) { #if Z_FEATURE_MULTI_THREAD == 1 return _z_mutex_lock((_z_mutex_t *)&ct->_mutex); #else _ZP_UNUSED(ct); return _Z_RES_OK; #endif } static inline z_result_t _z_cancellation_token_unlock(const _z_cancellation_token_t *ct) { #if Z_FEATURE_MULTI_THREAD == 1 return _z_mutex_unlock((_z_mutex_t *)&ct->_mutex); #else _ZP_UNUSED(ct); return _Z_RES_OK; #endif } static inline bool _z_unsafe_cancellation_token_has_started_cancel(const _z_cancellation_token_t *ct) { return !_z_sync_group_notifier_check(&ct->_handlers._cancel_sync_notifier); } bool _z_cancellation_token_is_cancelled(const _z_cancellation_token_t *ct) { return _z_sync_group_is_closed(&ct->_sync_group); } z_result_t _z_cancellation_token_call_handlers(_z_cancellation_token_t *ct) { bool set_cancel_result = false; if (_z_cancellation_token_lock(ct) != _Z_RES_OK) { return _Z_ERR_SYSTEM_GENERIC; } set_cancel_result = _z_sync_group_notifier_check(&ct->_handlers._cancel_sync_notifier); _z_cancellation_handlers_storage_t s = ct->_handlers; ct->_handlers = _z_cancellation_handlers_storage_null(); _z_cancellation_token_unlock(ct); if (set_cancel_result) { _z_cancellation_handlers_storage_cancel(&s, &ct->_cancel_result); if (ct->_cancel_result != _Z_RES_OK) { _z_sync_group_close(&ct->_sync_group); } } return _Z_RES_OK; } z_result_t _z_cancellation_token_cancel(_z_cancellation_token_t *ct) { _Z_RETURN_IF_ERR(_z_cancellation_token_call_handlers(ct)); _z_sync_group_wait(&ct->_sync_group); return ct->_cancel_result; } z_result_t _z_cancellation_token_cancel_with_timeout(_z_cancellation_token_t *ct, uint32_t timeout_ms) { z_clock_t deadline = z_clock_now(); z_clock_advance_ms(&deadline, timeout_ms); _Z_RETURN_IF_ERR(_z_cancellation_token_call_handlers(ct)); if (_z_sync_group_wait_deadline(&ct->_sync_group, &deadline) == Z_ETIMEDOUT) { return Z_ETIMEDOUT; } else { return ct->_cancel_result; } } void _z_cancellation_token_clear(_z_cancellation_token_t *ct) { #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&ct->_mutex); #endif _z_cancellation_token_clear_all_except_mutex(ct); } z_result_t _z_cancellation_token_add_on_cancel_handler(_z_cancellation_token_t *ct, _z_cancellation_token_on_cancel_handler_t *handler, size_t *out_handler_id) { _Z_RETURN_IF_ERR(_z_cancellation_token_lock(ct)); z_result_t ret = _z_unsafe_cancellation_token_has_started_cancel(ct) ? Z_ERR_CANCELLED : _z_cancellation_handlers_storage_add(&ct->_handlers, handler, out_handler_id); _z_cancellation_token_unlock(ct); return ret; } z_result_t _z_cancellation_token_remove_on_cancel_handler(_z_cancellation_token_t *ct, size_t handler_id) { _Z_RETURN_IF_ERR(_z_cancellation_token_lock(ct)); _z_cancellation_handlers_storage_remove(&ct->_handlers, handler_id); _z_cancellation_token_unlock(ct); return _Z_RES_OK; } z_result_t _z_cancellation_token_get_notifier(_z_cancellation_token_t *ct, _z_sync_group_notifier_t *notifier) { z_result_t ret = _z_sync_group_create_notifier(&ct->_sync_group, notifier); return ret == Z_SYNC_GROUP_CLOSED ? Z_ERR_CANCELLED : ret; } ================================================ FILE: src/session/interest.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast/lease.h" #include "zenoh-pico/utils/logging.h" static z_result_t _z_interest_send_decl_resource(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_resource_slist_t *res_list = _z_resource_slist_clone(zn->_local_resources); _z_session_mutex_unlock(zn); _z_resource_slist_t *xs = res_list; while (xs != NULL) { _z_resource_t *res = _z_resource_slist_value(xs); // Check if key is concerned if (restr_key == NULL || _z_keyexpr_intersects(restr_key, &res->_key)) { // Build the declare message to send on the wire _z_wireexpr_t wireexpr = _z_keyexpr_alias_to_wire(&res->_key); _z_declaration_t declaration = _z_make_decl_keyexpr(res->_id, &wireexpr); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_some(interest_id)); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, peer); _z_n_msg_clear(&n_msg); if (ret != _Z_RES_OK) { _z_resource_slist_free(&res_list); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } } xs = _z_resource_slist_next(xs); } _z_resource_slist_free(&res_list); return _Z_RES_OK; } #if Z_FEATURE_SUBSCRIPTION == 1 static z_result_t _z_interest_send_decl_subscriber(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_subscription_rc_slist_t *sub_list = _z_subscription_rc_slist_clone(zn->_subscriptions); _z_session_mutex_unlock(zn); _z_subscription_rc_slist_t *xs = sub_list; while (xs != NULL) { _z_subscription_rc_t *sub = _z_subscription_rc_slist_value(xs); // Check if key is concerned if (restr_key == NULL || _z_keyexpr_intersects(restr_key, &_Z_RC_IN_VAL(sub)->_key._inner)) { // Build the declare message to send on the wire _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(sub)->_key, zn); _z_declaration_t declaration = _z_make_decl_subscriber(&wireexpr, _Z_RC_IN_VAL(sub)->_id); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_some(interest_id)); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, peer); _z_n_msg_clear(&n_msg); if (ret != _Z_RES_OK) { _z_subscription_rc_slist_free(&sub_list); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } } xs = _z_subscription_rc_slist_next(xs); } _z_subscription_rc_slist_free(&sub_list); return _Z_RES_OK; } #else static z_result_t _z_interest_send_decl_subscriber(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _ZP_UNUSED(zn); _ZP_UNUSED(interest_id); _ZP_UNUSED(peer); _ZP_UNUSED(restr_key); return _Z_RES_OK; } #endif #if Z_FEATURE_QUERYABLE == 1 static z_result_t _z_interest_send_decl_queryable(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_session_queryable_rc_slist_t *qle_list = _z_session_queryable_rc_slist_clone(zn->_local_queryable); _z_session_mutex_unlock(zn); _z_session_queryable_rc_slist_t *xs = qle_list; while (xs != NULL) { _z_session_queryable_rc_t *qle = _z_session_queryable_rc_slist_value(xs); // Check if key is concerned if (restr_key == NULL || _z_keyexpr_intersects(restr_key, &_Z_RC_IN_VAL(qle)->_key._inner)) { // Build the declare message to send on the wire _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(qle)->_key, zn); _z_declaration_t declaration = _z_make_decl_queryable( &wireexpr, _Z_RC_IN_VAL(qle)->_id, _Z_RC_IN_VAL(qle)->_complete, _Z_QUERYABLE_DISTANCE_DEFAULT); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_some(interest_id)); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, peer); _z_n_msg_clear(&n_msg); if (ret != _Z_RES_OK) { _z_session_queryable_rc_slist_free(&qle_list); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } } xs = _z_session_queryable_rc_slist_next(xs); } _z_session_queryable_rc_slist_free(&qle_list); return _Z_RES_OK; } #else static z_result_t _z_interest_send_decl_queryable(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _ZP_UNUSED(zn); _ZP_UNUSED(interest_id); _ZP_UNUSED(peer); _ZP_UNUSED(restr_key); return _Z_RES_OK; } #endif #if Z_FEATURE_LIVELINESS == 1 static z_result_t _z_interest_send_decl_token(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_declared_keyexpr_intmap_t token_list = _z_declared_keyexpr_intmap_clone(&zn->_local_tokens); _z_session_mutex_unlock(zn); _z_declared_keyexpr_intmap_iterator_t iter = _z_declared_keyexpr_intmap_iterator_make(&token_list); while (_z_declared_keyexpr_intmap_iterator_next(&iter)) { uint32_t id = (uint32_t)_z_declared_keyexpr_intmap_iterator_key(&iter); // Check if key is concerned if (restr_key == NULL || _z_keyexpr_intersects(restr_key, &_z_declared_keyexpr_intmap_iterator_value(&iter)->_inner)) { // Build the declare message to send on the wire _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(_z_declared_keyexpr_intmap_iterator_value(&iter), zn); _z_declaration_t declaration = _z_make_decl_token(&wireexpr, id); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, _z_optional_id_make_some(interest_id)); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, peer); _z_n_msg_clear(&n_msg); if (ret != _Z_RES_OK) { _z_declared_keyexpr_intmap_clear(&token_list); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } } } _z_declared_keyexpr_intmap_clear(&token_list); return _Z_RES_OK; } #else static z_result_t _z_interest_send_decl_token(_z_session_t *zn, uint32_t interest_id, void *peer, const _z_keyexpr_t *restr_key) { _ZP_UNUSED(zn); _ZP_UNUSED(interest_id); _ZP_UNUSED(peer); _ZP_UNUSED(restr_key); return _Z_RES_OK; } #endif static z_result_t _z_interest_send_declare_final(_z_session_t *zn, uint32_t interest_id, void *peer) { _z_declaration_t decl = _z_make_decl_final(); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, decl, _z_optional_id_make_some(interest_id)); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, peer); _z_n_msg_clear(&n_msg); if (ret != _Z_RES_OK) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } return _Z_RES_OK; } z_result_t _z_interest_push_declarations_to_peer(_z_session_t *zn, void *peer) { _Z_RETURN_IF_ERR(_z_interest_send_decl_resource(zn, 0, peer, NULL)); _Z_RETURN_IF_ERR(_z_interest_send_decl_subscriber(zn, 0, peer, NULL)); _Z_RETURN_IF_ERR(_z_interest_send_decl_queryable(zn, 0, peer, NULL)); _Z_RETURN_IF_ERR(_z_interest_send_decl_token(zn, 0, peer, NULL)); _Z_RETURN_IF_ERR(_z_interest_send_declare_final(zn, 0, peer)); return _Z_RES_OK; } z_result_t _z_interest_pull_resource_from_peers(_z_session_t *zn) { // Retrieve all current resource declarations uint32_t eid = _z_get_entity_id(zn); uint8_t flags = _Z_INTEREST_FLAG_KEYEXPRS | _Z_INTEREST_FLAG_CURRENT; // Send message on the network _z_interest_t interest = _z_make_interest(NULL, eid, flags); _z_network_message_t n_msg; _z_n_msg_make_interest(&n_msg, interest); z_result_t ret = _z_send_n_msg(zn, &n_msg, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); _z_n_msg_clear(&n_msg); return ret; } #if Z_FEATURE_INTEREST == 1 void _z_declare_data_clear(_z_declare_data_t *data) { _z_keyexpr_clear(&data->_key); } size_t _z_declare_data_size(_z_declare_data_t *data) { _ZP_UNUSED(data); return sizeof(_z_declare_data_t); } void _z_declare_data_copy(_z_declare_data_t *dst, const _z_declare_data_t *src) { dst->_id = src->_id; dst->_type = src->_type; dst->_peer = src->_peer; _z_keyexpr_copy(&dst->_key, &src->_key); } bool _z_declare_data_eq(const _z_declare_data_t *left, const _z_declare_data_t *right) { return ((left->_id == right->_id) && (left->_type == right->_type)); } bool _z_session_interest_eq(const _z_session_interest_t *one, const _z_session_interest_t *two) { return one->_id == two->_id; } void _z_session_interest_clear(_z_session_interest_t *intr) { _z_keyexpr_clear(&intr->_key); _z_void_rc_drop(&intr->_arg); } /*------------------ interest ------------------*/ static _z_session_interest_rc_t *__z_get_interest_by_id(_z_session_interest_rc_slist_t *intrs, const _z_zint_t id) { _z_session_interest_rc_t *ret = NULL; _z_session_interest_rc_slist_t *xs = intrs; while (xs != NULL) { _z_session_interest_rc_t *intr = _z_session_interest_rc_slist_value(xs); if (id == _Z_RC_IN_VAL(intr)->_id) { ret = intr; break; } xs = _z_session_interest_rc_slist_next(xs); } return ret; } static _z_session_interest_rc_slist_t *__z_get_interest_by_key_and_flags(_z_session_interest_rc_slist_t *intrs, uint8_t flags, const _z_keyexpr_t *key, _z_optional_id_t interest_id) { _z_session_interest_rc_slist_t *ret = NULL; _z_session_interest_rc_slist_t *xs = intrs; while (xs != NULL) { _z_session_interest_rc_t *intr = _z_session_interest_rc_slist_value(xs); if ((_Z_RC_IN_VAL(intr)->_flags & flags) == 0) { xs = _z_session_interest_rc_slist_next(xs); continue; } // consider only interests with matching id if specified (which corresponds to CURRENT interest response) // ignore 0 id, since it is the one initially used by peers for declarations propagation if (interest_id.has_value && interest_id.value != 0 && interest_id.value != _Z_RC_IN_VAL(intr)->_id) { xs = _z_session_interest_rc_slist_next(xs); continue; } bool is_matching = _z_session_interest_is_aggregate(_Z_RC_IN_VAL(intr)) ? _z_keyexpr_equals(&_Z_RC_IN_VAL(intr)->_key, key) : _z_keyexpr_intersects(&_Z_RC_IN_VAL(intr)->_key, key); if (is_matching) { ret = _z_session_interest_rc_slist_push_empty(ret); _z_session_interest_rc_t *new_intr = _z_session_interest_rc_slist_value(ret); *new_intr = _z_session_interest_rc_clone(intr); } xs = _z_session_interest_rc_slist_next(xs); } return ret; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static _z_session_interest_rc_t *__unsafe_z_get_interest_by_id(_z_session_t *zn, const _z_zint_t id) { _z_session_interest_rc_slist_t *intrs = zn->_local_interests; return __z_get_interest_by_id(intrs, id); } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static _z_session_interest_rc_slist_t *__unsafe_z_get_interest_by_key_and_flags(_z_session_t *zn, uint8_t flags, const _z_keyexpr_t *key, _z_optional_id_t interest_id) { _z_session_interest_rc_slist_t *intrs = zn->_local_interests; return __z_get_interest_by_key_and_flags(intrs, flags, key, interest_id); } _z_session_interest_rc_t *_z_get_interest_by_id(_z_session_t *zn, const _z_zint_t id) { _z_session_mutex_lock(zn); _z_session_interest_rc_t *intr = __unsafe_z_get_interest_by_id(zn, id); _z_session_mutex_unlock(zn); return intr; } _z_session_interest_rc_t *_z_register_interest(_z_session_t *zn, _z_session_interest_t *intr) { _Z_DEBUG(">>> Allocating interest for (%.*s)", (int)_z_string_len(&intr->_key._keyexpr), _z_string_data(&intr->_key._keyexpr)); _z_session_interest_rc_t *ret = NULL; if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { _Z_WARN("Failed to acquire session mutex for registering interest for (%.*s) - session is closed", (int)_z_string_len(&intr->_key._keyexpr), _z_string_data(&intr->_key._keyexpr)); return ret; } zn->_local_interests = _z_session_interest_rc_slist_push_empty(zn->_local_interests); ret = _z_session_interest_rc_slist_value(zn->_local_interests); *ret = _z_session_interest_rc_new_from_val(intr); _z_session_mutex_unlock(zn); return ret; } static z_result_t _unsafe_z_register_declare(_z_session_t *zn, const _z_keyexpr_t *key, uint32_t id, uint8_t type, bool complete, _z_transport_peer_common_t *peer) { zn->_remote_declares = _z_declare_data_slist_push_empty(zn->_remote_declares); _z_declare_data_t *decl = _z_declare_data_slist_value(zn->_remote_declares); _z_keyexpr_copy(&decl->_key, key); decl->_id = id; decl->_type = type; decl->_complete = complete; decl->_peer = peer; return _Z_RES_OK; } static _z_declare_data_t *_unsafe_z_get_declare(_z_session_t *zn, uint32_t id, uint8_t type) { _z_declare_data_slist_t *xs = zn->_remote_declares; _z_declare_data_t comp = {._key = _z_keyexpr_null(), ._id = id, ._type = type, ._complete = false}; while (xs != NULL) { _z_declare_data_t *decl = _z_declare_data_slist_value(xs); if (_z_declare_data_eq(&comp, decl)) { return decl; } xs = _z_declare_data_slist_next(xs); } return NULL; } static z_result_t _unsafe_z_unregister_declare(_z_session_t *zn, uint32_t id, uint8_t type) { _z_declare_data_t decl = {._key = _z_keyexpr_null(), ._id = id, ._type = type, ._complete = false}; zn->_remote_declares = _z_declare_data_slist_drop_first_filter(zn->_remote_declares, _z_declare_data_eq, &decl); return _Z_RES_OK; } z_result_t _z_interest_process_declares(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { const _z_wireexpr_t *decl_key = NULL; _z_interest_msg_t msg; uint8_t flags = 0; uint8_t decl_type = 0; msg.is_complete = false; switch (decl->_decl._tag) { case _Z_DECL_SUBSCRIBER: msg.type = _Z_INTEREST_MSG_TYPE_DECL_SUBSCRIBER; msg.id = decl->_decl._body._decl_subscriber._id; decl_key = &decl->_decl._body._decl_subscriber._keyexpr; decl_type = _Z_DECLARE_TYPE_SUBSCRIBER; flags = _Z_INTEREST_FLAG_SUBSCRIBERS; break; case _Z_DECL_QUERYABLE: msg.type = _Z_INTEREST_MSG_TYPE_DECL_QUERYABLE; msg.id = decl->_decl._body._decl_queryable._id; decl_key = &decl->_decl._body._decl_queryable._keyexpr; decl_type = _Z_DECLARE_TYPE_QUERYABLE; flags = _Z_INTEREST_FLAG_QUERYABLES; msg.is_complete = decl->_decl._body._decl_queryable._ext_queryable_info._complete; break; case _Z_DECL_TOKEN: msg.type = _Z_INTEREST_MSG_TYPE_DECL_TOKEN; msg.id = decl->_decl._body._decl_token._id; decl_key = &decl->_decl._body._decl_token._keyexpr; decl_type = _Z_DECLARE_TYPE_TOKEN; flags = _Z_INTEREST_FLAG_TOKENS; break; default: _Z_ERROR_RETURN(_Z_ERR_MESSAGE_ZENOH_DECLARATION_UNKNOWN); } // Retrieve key _z_keyexpr_t key; if (_z_get_keyexpr_from_wireexpr(zn, &key, decl_key, peer, true) != _Z_RES_OK) { _Z_ERROR_RETURN(_Z_ERR_KEYEXPR_UNKNOWN); } _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&key)); msg.key = &key; // NOTE: it is possible that it is a redeclare of an existing entity - so we might need to update it _z_declare_data_t *prev_decl = _unsafe_z_get_declare(zn, msg.id, decl_type); if (prev_decl != NULL) { // possible change in queryable completness prev_decl->_complete = msg.is_complete; } else { // register new declare _unsafe_z_register_declare(zn, &key, msg.id, decl_type, msg.is_complete, peer); } // Retrieve interests _z_session_interest_rc_slist_t *intrs = __unsafe_z_get_interest_by_key_and_flags(zn, flags, &key, decl->_interest_id); _z_session_mutex_unlock(zn); // update interests with new value _z_session_interest_rc_slist_t *xs = intrs; while (xs != NULL) { _z_session_interest_rc_t *intr = _z_session_interest_rc_slist_value(xs); if (_Z_RC_IN_VAL(intr)->_callback != NULL) { _Z_RC_IN_VAL(intr)->_callback(&msg, peer, _Z_RC_IN_VAL(&_Z_RC_IN_VAL(intr)->_arg)); } xs = _z_session_interest_rc_slist_next(xs); } // Clean up _z_keyexpr_clear(&key); _z_session_interest_rc_slist_free(&intrs); return _Z_RES_OK; } z_result_t _z_interest_process_undeclares(_z_session_t *zn, const _z_declaration_t *decl, _z_transport_peer_common_t *peer) { _z_interest_msg_t msg = {0}; uint8_t flags = 0; uint8_t decl_type = 0; switch (decl->_tag) { case _Z_UNDECL_SUBSCRIBER: msg.type = _Z_INTEREST_MSG_TYPE_UNDECL_SUBSCRIBER; msg.id = decl->_body._undecl_subscriber._id; decl_type = _Z_DECLARE_TYPE_SUBSCRIBER; flags = _Z_INTEREST_FLAG_SUBSCRIBERS; break; case _Z_UNDECL_QUERYABLE: msg.type = _Z_INTEREST_MSG_TYPE_UNDECL_QUERYABLE; msg.id = decl->_body._undecl_queryable._id; decl_type = _Z_DECLARE_TYPE_QUERYABLE; flags = _Z_INTEREST_FLAG_QUERYABLES; break; case _Z_UNDECL_TOKEN: msg.type = _Z_INTEREST_MSG_TYPE_UNDECL_TOKEN; msg.id = decl->_body._undecl_token._id; decl_type = _Z_DECLARE_TYPE_TOKEN; flags = _Z_INTEREST_FLAG_TOKENS; break; default: _Z_ERROR_RETURN(_Z_ERR_MESSAGE_ZENOH_DECLARATION_UNKNOWN); } _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); // Retrieve declare data _z_declare_data_t *prev_decl = _unsafe_z_get_declare(zn, msg.id, decl_type); if (prev_decl == NULL) { _z_session_mutex_unlock(zn); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_ZENOH_DECLARATION_UNKNOWN); } _z_session_interest_rc_slist_t *intrs = __unsafe_z_get_interest_by_key_and_flags(zn, flags, &prev_decl->_key, _z_optional_id_make_none()); // Remove declare _unsafe_z_unregister_declare(zn, msg.id, decl_type); _z_session_mutex_unlock(zn); // Parse session_interest list _z_session_interest_rc_slist_t *xs = intrs; while (xs != NULL) { _z_session_interest_rc_t *intr = _z_session_interest_rc_slist_value(xs); if (_Z_RC_IN_VAL(intr)->_callback != NULL) { _Z_RC_IN_VAL(intr)->_callback(&msg, peer, _Z_RC_IN_VAL(&_Z_RC_IN_VAL(intr)->_arg)); } xs = _z_session_interest_rc_slist_next(xs); } // Clean up _z_session_interest_rc_slist_free(&intrs); return _Z_RES_OK; } void _z_unregister_interest(_z_session_t *zn, _z_session_interest_rc_t *intr) { _z_session_mutex_lock(zn); zn->_local_interests = _z_session_interest_rc_slist_drop_first_filter(zn->_local_interests, _z_session_interest_rc_eq, intr); _z_session_mutex_unlock(zn); } void _z_interest_init(_z_session_t *zn) { _z_session_mutex_lock(zn); zn->_local_interests = NULL; zn->_remote_declares = NULL; _z_session_mutex_unlock(zn); } void _z_flush_interest(_z_session_t *zn) { _z_session_mutex_lock(zn); _z_session_interest_rc_slist_free(&zn->_local_interests); _z_declare_data_slist_free(&zn->_remote_declares); _z_session_mutex_unlock(zn); } z_result_t _z_interest_process_declare_final(_z_session_t *zn, uint32_t id, _z_transport_peer_common_t *peer) { _z_interest_msg_t msg = {.type = _Z_INTEREST_MSG_TYPE_FINAL, .id = id}; // Retrieve interest _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_session_interest_rc_t *intr = __unsafe_z_get_interest_by_id(zn, id); _z_session_mutex_unlock(zn); if (intr == NULL) { return _Z_RES_OK; } // Trigger callback if (_Z_RC_IN_VAL(intr)->_callback != NULL) { _Z_RC_IN_VAL(intr)->_callback(&msg, peer, _Z_RC_IN_VAL(&_Z_RC_IN_VAL(intr)->_arg)); } return _Z_RES_OK; } z_result_t _z_interest_process_interest_final(_z_session_t *zn, uint32_t id) { _ZP_UNUSED(zn); _ZP_UNUSED(id); // TODO: Update future masks return _Z_RES_OK; } z_result_t _z_interest_process_interest(_z_session_t *zn, const _z_wireexpr_t *wireexpr, uint32_t id, uint8_t flags, _z_transport_peer_common_t *peer) { // Check transport type if (zn->_tp._type == _Z_TRANSPORT_UNICAST_TYPE) { return _Z_RES_OK; // Nothing to do on unicast } // Push a join in case it's a new node _Z_RETURN_IF_ERR(_zp_multicast_send_join(&zn->_tp._transport._multicast)); _z_keyexpr_t restr_key = _z_keyexpr_null(); if (_Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_RESTRICTED)) { _Z_RETURN_IF_ERR(_z_get_keyexpr_from_wireexpr(zn, &restr_key, wireexpr, peer, true)); } _z_keyexpr_t *restr_key_opt = _z_keyexpr_check(&restr_key) ? &restr_key : NULL; z_result_t ret = _Z_RES_OK; // Current flags process if (_Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_CURRENT)) { // Send all declare if (ret == _Z_RES_OK && _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_KEYEXPRS)) { _Z_DEBUG("Sending declare resources"); ret = _z_interest_send_decl_resource(zn, id, NULL, restr_key_opt); } if (ret == _Z_RES_OK && _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_SUBSCRIBERS)) { _Z_DEBUG("Sending declare subscribers"); ret = _z_interest_send_decl_subscriber(zn, id, NULL, restr_key_opt); } if (ret == _Z_RES_OK && _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_QUERYABLES)) { _Z_DEBUG("Sending declare queryables"); ret = _z_interest_send_decl_queryable(zn, id, NULL, restr_key_opt); } if (ret == _Z_RES_OK && _Z_HAS_FLAG(flags, _Z_INTEREST_FLAG_TOKENS)) { _Z_DEBUG("Sending declare tokens"); ret = _z_interest_send_decl_token(zn, id, NULL, restr_key_opt); } // Send final declare _Z_SET_IF_OK(ret, _z_interest_send_declare_final(zn, id, NULL)); } _z_keyexpr_clear(&restr_key); return ret; } void _z_interest_peer_disconnected(_z_session_t *zn, _z_transport_peer_common_t *peer) { // Clone session interest list if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { return; } _z_session_interest_rc_slist_t *intrs = _z_session_interest_rc_slist_clone(zn->_local_interests); _z_session_mutex_unlock(zn); // Parse session_interest list _z_interest_msg_t msg = {.id = 0, .type = _Z_INTEREST_MSG_TYPE_CONNECTION_DROPPED}; _z_session_interest_rc_slist_t *xs = intrs; while (xs != NULL) { _z_session_interest_rc_t *intr = _z_session_interest_rc_slist_value(xs); if (_Z_RC_IN_VAL(intr)->_callback != NULL) { _Z_RC_IN_VAL(intr)->_callback(&msg, peer, _Z_RC_IN_VAL(&_Z_RC_IN_VAL(intr)->_arg)); } xs = _z_session_interest_rc_slist_next(xs); } // Clean up _z_session_interest_rc_slist_free(&intrs); } void _z_interest_replay_declare(_z_session_t *zn, _z_session_interest_t *interest) { if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { return; } _z_declare_data_slist_t *res_list = _z_declare_data_slist_clone(zn->_remote_declares); _z_session_mutex_unlock(zn); _z_declare_data_slist_t *xs = res_list; while (xs != NULL) { _z_declare_data_t *res = _z_declare_data_slist_value(xs); bool is_matching = _z_session_interest_is_aggregate(interest) ? _z_keyexpr_equals(&interest->_key, &res->_key) : _z_keyexpr_intersects(&interest->_key, &res->_key); if (is_matching) { _z_interest_msg_t msg = {0}; msg.key = &res->_key; msg.is_complete = res->_complete; msg.id = res->_id; switch (res->_type) { default: break; case _Z_DECLARE_TYPE_QUERYABLE: msg.type = _Z_INTEREST_MSG_TYPE_DECL_QUERYABLE; break; case _Z_DECLARE_TYPE_SUBSCRIBER: msg.type = _Z_INTEREST_MSG_TYPE_DECL_SUBSCRIBER; break; case _Z_DECLARE_TYPE_TOKEN: msg.type = _Z_INTEREST_MSG_TYPE_DECL_TOKEN; break; } interest->_callback(&msg, res->_peer, _Z_RC_IN_VAL(&interest->_arg)); } xs = _z_declare_data_slist_next(xs); } _z_declare_data_slist_free(&res_list); } #else void _z_interest_init(_z_session_t *zn) { _ZP_UNUSED(zn); } void _z_flush_interest(_z_session_t *zn) { _ZP_UNUSED(zn); } z_result_t _z_interest_process_declares(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(decl); _ZP_UNUSED(peer); return _Z_RES_OK; } z_result_t _z_interest_process_undeclares(_z_session_t *zn, const _z_declaration_t *decl, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(decl); _ZP_UNUSED(peer); return _Z_RES_OK; } z_result_t _z_interest_process_declare_final(_z_session_t *zn, uint32_t id, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(id); _ZP_UNUSED(peer); return _Z_RES_OK; } z_result_t _z_interest_process_interest_final(_z_session_t *zn, uint32_t id) { _ZP_UNUSED(zn); _ZP_UNUSED(id); return _Z_RES_OK; } z_result_t _z_interest_process_interest(_z_session_t *zn, _z_wireexpr_t *wireexpr, uint32_t id, uint8_t flags, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(wireexpr); _ZP_UNUSED(id); _ZP_UNUSED(flags); _ZP_UNUSED(peer); return _Z_RES_OK; } void _z_interest_peer_disconnected(_z_session_t *zn, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(peer); } #endif ================================================ FILE: src/session/keyexpr.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/session/keyexpr.h" #include #include #include #include #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/string.h" z_result_t _z_keyexpr_wire_declaration_new(_z_keyexpr_wire_declaration_t *declaration, const _z_string_t *keyexpr, const _z_session_rc_t *session) { *declaration = _z_keyexpr_wire_declaration_null(); z_result_t ret = _z_declare_resource(_Z_RC_IN_VAL(session), keyexpr, &declaration->_id); if (ret == _Z_RES_OK) { declaration->_prefix_len = (uint16_t)_z_string_len(keyexpr); declaration->_session = _z_session_rc_clone_as_weak(session); } return ret; } z_result_t _z_keyexpr_wire_declaration_undeclare(_z_keyexpr_wire_declaration_t *declaration) { z_result_t ret = _Z_RES_OK; if (_Z_RC_IS_NULL(&declaration->_session)) { return ret; } _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(&declaration->_session); if (!_Z_RC_IS_NULL(&session_rc)) { ret = _z_undeclare_resource(_Z_RC_IN_VAL(&session_rc), declaration->_id); _z_session_rc_drop(&session_rc); } declaration->_id = Z_RESOURCE_ID_NONE; declaration->_prefix_len = 0; _z_session_weak_drop(&declaration->_session); return ret; } void _z_keyexpr_wire_declaration_clear(_z_keyexpr_wire_declaration_t *declaration) { _z_keyexpr_wire_declaration_undeclare(declaration); } _z_keyexpr_t _z_keyexpr_alias_from_string(const _z_string_t *str) { _z_keyexpr_t ke = _z_keyexpr_null(); ke._keyexpr = _z_string_alias(*str); return ke; } _z_keyexpr_t _z_keyexpr_alias_from_substr(const char *str, size_t len) { _z_keyexpr_t ke = _z_keyexpr_null(); ke._keyexpr = _z_string_alias_substr(str, len); return ke; } _z_declared_keyexpr_t _z_declared_keyexpr_alias_from_string(const _z_string_t *str) { _z_declared_keyexpr_t ke = _z_declared_keyexpr_null(); ke._inner = _z_keyexpr_alias_from_string(str); return ke; } _z_declared_keyexpr_t _z_declared_keyexpr_alias_from_substr(const char *str, size_t len) { _z_declared_keyexpr_t ke = _z_declared_keyexpr_null(); ke._inner = _z_keyexpr_alias_from_substr(str, len); return ke; } z_result_t _z_keyexpr_from_string(_z_keyexpr_t *dst, const _z_string_t *str) { return _z_keyexpr_from_substr(dst, _z_string_data(str), _z_string_len(str)); } z_result_t _z_keyexpr_from_substr(_z_keyexpr_t *dst, const char *str, size_t len) { *dst = _z_keyexpr_null(); dst->_keyexpr = _z_string_copy_from_substr(str, len); return _z_string_check(&dst->_keyexpr) ? _Z_RES_OK : _Z_ERR_SYSTEM_OUT_OF_MEMORY; } z_result_t _z_declared_keyexpr_copy(_z_declared_keyexpr_t *dst, const _z_declared_keyexpr_t *src) { *dst = _z_declared_keyexpr_null(); _Z_RETURN_IF_ERR(_z_keyexpr_copy(&dst->_inner, &src->_inner)); if (!_Z_RC_IS_NULL(&src->_declaration)) { dst->_declaration = _z_keyexpr_wire_declaration_rc_clone(&src->_declaration); } return _Z_RES_OK; } z_result_t _z_declared_keyexpr_move(_z_declared_keyexpr_t *dst, _z_declared_keyexpr_t *src) { *dst = _z_declared_keyexpr_null(); _Z_RETURN_IF_ERR(_z_keyexpr_move(&dst->_inner, &src->_inner)); dst->_declaration = src->_declaration; src->_declaration = _z_keyexpr_wire_declaration_rc_null(); return _Z_RES_OK; } /*------------------ Canonize helpers ------------------*/ zp_keyexpr_canon_status_t __zp_canon_prefix(const char *start, size_t *len) { zp_keyexpr_canon_status_t ret = Z_KEYEXPR_CANON_SUCCESS; bool in_big_wild = false; char const *chunk_start = start; const char *end = _z_cptr_char_offset(start, (ptrdiff_t)(*len)); char const *next_slash; do { next_slash = memchr(chunk_start, '/', _z_ptr_char_diff(end, chunk_start)); const char *chunk_end = next_slash ? next_slash : end; size_t chunk_len = _z_ptr_char_diff(chunk_end, chunk_start); switch (chunk_len) { case 0: { ret = Z_KEYEXPR_CANON_EMPTY_CHUNK; } break; case 1: { if (in_big_wild && (chunk_start[0] == '*')) { *len = _z_ptr_char_diff(chunk_start, start) - (size_t)3; ret = Z_KEYEXPR_CANON_SINGLE_STAR_AFTER_DOUBLE_STAR; } else { chunk_start = _z_cptr_char_offset(chunk_end, 1); continue; } } break; case 2: if (chunk_start[1] == '*') { if (chunk_start[0] == '$') { *len = _z_ptr_char_diff(chunk_start, start); ret = Z_KEYEXPR_CANON_LONE_DOLLAR_STAR; } else if (chunk_start[0] == '*') { if (in_big_wild) { *len = _z_ptr_char_diff(chunk_start, start) - (size_t)3; ret = Z_KEYEXPR_CANON_DOUBLE_STAR_AFTER_DOUBLE_STAR; } else { chunk_start = _z_cptr_char_offset(chunk_end, 1); in_big_wild = true; continue; } } else { // Do nothing. Required to be compliant with MISRA 15.7 rule } } else { // Do nothing. Required to be compliant with MISRA 15.7 rule } break; default: break; } unsigned char in_dollar = 0; for (char const *c = chunk_start; (c < chunk_end) && (ret == Z_KEYEXPR_CANON_SUCCESS); c = _z_cptr_char_offset(c, 1)) { switch (c[0]) { case '#': case '?': { ret = Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK; } break; case '$': { if (in_dollar != (unsigned char)0) { ret = Z_KEYEXPR_CANON_DOLLAR_AFTER_DOLLAR_OR_STAR; } else { in_dollar = in_dollar + (unsigned char)1; } } break; case '*': { if (in_dollar != (unsigned char)1) { ret = Z_KEYEXPR_CANON_STARS_IN_CHUNK; } else { in_dollar = in_dollar + (unsigned char)2; } } break; default: { if (in_dollar == (unsigned char)1) { ret = Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR; } else { in_dollar = 0; } } break; } } if (ret == Z_KEYEXPR_CANON_SUCCESS) { if (in_dollar == (unsigned char)1) { ret = Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR; } else { chunk_start = _z_cptr_char_offset(chunk_end, 1); in_big_wild = false; } } } while ((chunk_start < end) && (ret == Z_KEYEXPR_CANON_SUCCESS)); if (ret == Z_KEYEXPR_CANON_SUCCESS) { if (chunk_start <= end) { ret = Z_KEYEXPR_CANON_EMPTY_CHUNK; } } return ret; } void __zp_singleify(char *start, size_t *len, const char *needle) { const char *end = _z_cptr_char_offset(start, (ptrdiff_t)(*len)); bool right_after_needle = false; char *reader = start; while (reader < end) { size_t pos = _z_str_startswith(reader, needle); if (pos != (size_t)0) { if (right_after_needle == true) { break; } right_after_needle = true; reader = _z_ptr_char_offset(reader, (ptrdiff_t)pos); } else { right_after_needle = false; reader = _z_ptr_char_offset(reader, 1); } } char *writer = reader; while (reader < end) { size_t pos = _z_str_startswith(reader, needle); if (pos != (size_t)0) { if (right_after_needle == false) { for (size_t i = 0; i < pos; i++) { writer[i] = reader[i]; } writer = _z_ptr_char_offset(writer, (ptrdiff_t)pos); } right_after_needle = true; reader = _z_ptr_char_offset(reader, (ptrdiff_t)pos); } else { right_after_needle = false; *writer = *reader; writer = _z_ptr_char_offset(writer, 1); reader = _z_ptr_char_offset(reader, 1); } } *len = _z_ptr_char_diff(writer, start); } void __zp_ke_write_chunk(char **writer, const char *chunk, size_t len, const char *write_start) { if (writer[0] != write_start) { writer[0][0] = '/'; writer[0] = _z_ptr_char_offset(writer[0], 1); } (void)memmove(writer[0], chunk, len); writer[0] = _z_ptr_char_offset(writer[0], (ptrdiff_t)len); } /*------------------ Common helpers ------------------*/ typedef bool (*_z_ke_chunk_matcher)(_z_str_se_t l, _z_str_se_t r); enum _zp_wildness_t { _ZP_WILDNESS_ANY = 1, _ZP_WILDNESS_SUPERCHUNKS = 2, _ZP_WILDNESS_SUBCHUNK_DSL = 4 }; int8_t _zp_ke_wildness(_z_str_se_t ke, size_t *n_segments, size_t *n_verbatims) { const char *start = ke.start; const char *end = ke.end; int8_t result = 0; char prev_char = 0; for (char const *c = start; c < end; c = _z_cptr_char_offset(c, 1)) { switch (c[0]) { case '*': { result = result | (int8_t)_ZP_WILDNESS_ANY; if (prev_char == '*') { result = result | (int8_t)_ZP_WILDNESS_SUPERCHUNKS; } } break; case '$': { result = result | (int8_t)_ZP_WILDNESS_SUBCHUNK_DSL; } break; case '/': { *n_segments = *n_segments + (size_t)1; } break; case '@': { *n_verbatims = *n_verbatims + (size_t)1; } default: { // Do nothing } break; } prev_char = *c; } return result; } const char *_Z_DOUBLE_STAR = "**"; const char *_Z_DOLLAR_STAR = "$*"; zp_keyexpr_canon_status_t _z_keyexpr_canonize(char *start, size_t *len) { __zp_singleify(start, len, "$*"); size_t canon_len = *len; zp_keyexpr_canon_status_t ret = __zp_canon_prefix(start, &canon_len); if ((ret == Z_KEYEXPR_CANON_LONE_DOLLAR_STAR) || (ret == Z_KEYEXPR_CANON_SINGLE_STAR_AFTER_DOUBLE_STAR) || (ret == Z_KEYEXPR_CANON_DOUBLE_STAR_AFTER_DOUBLE_STAR)) { ret = Z_KEYEXPR_CANON_SUCCESS; const char *end = _z_cptr_char_offset(start, (ptrdiff_t)(*len)); char *reader = _z_ptr_char_offset(start, (ptrdiff_t)canon_len); const char *write_start = reader; char *writer = reader; char *next_slash = strchr(reader, '/'); char const *chunk_end = (next_slash != NULL) ? next_slash : end; bool in_big_wild = false; if ((_z_ptr_char_diff(chunk_end, reader) == 2) && (reader[1] == '*')) { if (reader[0] == '*') { in_big_wild = true; } else if (reader[0] == '$') { writer[0] = '*'; writer = _z_ptr_char_offset(writer, 1); } else { assert(false); // anything before "$*" or "**" must be part of the canon prefix } } else { assert(false); // anything before "$*" or "**" must be part of the canon prefix } while (next_slash != NULL) { reader = _z_ptr_char_offset(next_slash, 1); next_slash = memchr(reader, '/', _z_ptr_char_diff(end, reader)); chunk_end = next_slash ? next_slash : end; switch (_z_ptr_char_diff(chunk_end, reader)) { case 0: { ret = Z_KEYEXPR_CANON_EMPTY_CHUNK; } break; case 1: { if (reader[0] == '*') { __zp_ke_write_chunk(&writer, "*", 1, write_start); continue; } } break; case 2: { if (reader[1] == '*') { if (reader[0] == '$') { __zp_ke_write_chunk(&writer, "*", 1, write_start); continue; } else if (reader[0] == '*') { in_big_wild = true; continue; } else { // Do nothing. Required to be compliant with MISRA 15.7 rule } } } break; default: break; } unsigned char in_dollar = 0; for (char const *c = reader; (c < end) && (ret == Z_KEYEXPR_CANON_SUCCESS); c = _z_cptr_char_offset(c, 1)) { switch (*c) { case '#': case '?': { ret = Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK; } break; case '$': { if (in_dollar != (unsigned char)0) { ret = Z_KEYEXPR_CANON_DOLLAR_AFTER_DOLLAR_OR_STAR; } else { in_dollar = in_dollar + (unsigned char)1; } } break; case '*': { if (in_dollar != (unsigned char)1) { ret = Z_KEYEXPR_CANON_STARS_IN_CHUNK; } else { in_dollar = in_dollar + (unsigned char)2; } } break; default: { if (in_dollar == (unsigned char)1) { ret = Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR; } else { in_dollar = 0; } } break; } } if ((ret == Z_KEYEXPR_CANON_SUCCESS) && (in_dollar == (unsigned char)1)) { ret = Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR; } if (ret == Z_KEYEXPR_CANON_SUCCESS) { if (in_big_wild) { __zp_ke_write_chunk(&writer, _Z_DOUBLE_STAR, 2, write_start); } __zp_ke_write_chunk(&writer, reader, _z_ptr_char_diff(chunk_end, reader), write_start); in_big_wild = false; } } if (ret == Z_KEYEXPR_CANON_SUCCESS) { if (in_big_wild) { __zp_ke_write_chunk(&writer, _Z_DOUBLE_STAR, 2, write_start); } *len = _z_ptr_char_diff(writer, start); } } return ret; } zp_keyexpr_canon_status_t _z_keyexpr_is_canon(const char *start, size_t len) { return __zp_canon_prefix(start, &len); } z_result_t _z_keyexpr_concat(_z_keyexpr_t *key, const _z_keyexpr_t *left, const char *right, size_t len) { *key = _z_keyexpr_null(); size_t left_len = _z_string_len(&left->_keyexpr); if (left_len == 0) { return _z_keyexpr_from_substr(key, right, len); } const char *left_data = _z_string_data(&left->_keyexpr); if (left_data[left_len - 1] == '*' && len > 0 && right[0] == '*') { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _Z_RETURN_IF_ERR(_z_string_concat_substr(&key->_keyexpr, &left->_keyexpr, right, len, NULL, 0)); return _Z_RES_OK; } z_result_t _z_declared_keyexpr_concat(_z_declared_keyexpr_t *key, const _z_declared_keyexpr_t *left, const char *right, size_t len) { *key = _z_declared_keyexpr_null(); _Z_RETURN_IF_ERR(_z_keyexpr_concat(&key->_inner, &left->_inner, right, len)); if (!_Z_RC_IS_NULL(&left->_declaration)) { key->_declaration = _z_keyexpr_wire_declaration_rc_clone(&left->_declaration); } return _Z_RES_OK; } z_result_t _z_keyexpr_join(_z_keyexpr_t *key, const _z_keyexpr_t *left, const _z_keyexpr_t *right) { *key = _z_keyexpr_null(); _Z_RETURN_IF_ERR(_z_string_concat_substr(&key->_keyexpr, &left->_keyexpr, _z_string_data(&right->_keyexpr), _z_string_len(&right->_keyexpr), "/", 1)); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_canonize((char *)key->_keyexpr._slice.start, &key->_keyexpr._slice.len), _z_keyexpr_clear(key)); return _Z_RES_OK; } z_result_t _z_declared_keyexpr_join(_z_declared_keyexpr_t *key, const _z_declared_keyexpr_t *left, const _z_declared_keyexpr_t *right) { *key = _z_declared_keyexpr_null(); _Z_RETURN_IF_ERR(_z_keyexpr_join(&key->_inner, &left->_inner, &right->_inner)); if (!_Z_RC_IS_NULL(&left->_declaration)) { key->_declaration = _z_keyexpr_wire_declaration_rc_clone(&left->_declaration); } return _Z_RES_OK; } _z_wireexpr_t _z_declared_keyexpr_alias_to_wire(const _z_declared_keyexpr_t *key, const _z_session_t *session) { _z_wireexpr_t expr = _z_wireexpr_null(); const char *suffix_str = _z_string_data(&key->_inner._keyexpr); size_t suffix_len = _z_string_len(&key->_inner._keyexpr); if (!_Z_RC_IS_NULL(&key->_declaration) && _z_keyexpr_wire_declaration_is_declared_on_session(_Z_RC_IN_VAL(&key->_declaration), session)) { expr._id = _Z_RC_IN_VAL(&key->_declaration)->_id; expr._mapping = _Z_KEYEXPR_MAPPING_LOCAL; suffix_str = suffix_str + _Z_RC_IN_VAL(&key->_declaration)->_prefix_len; suffix_len -= _Z_RC_IN_VAL(&key->_declaration)->_prefix_len; } if (suffix_len > 0) { expr._suffix = _z_string_alias_substr(suffix_str, suffix_len); } return expr; } _z_wireexpr_t _z_keyexpr_alias_to_wire(const _z_keyexpr_t *key) { _z_wireexpr_t expr = _z_wireexpr_null(); expr._suffix = _z_string_alias(key->_keyexpr); return expr; } z_result_t _z_keyexpr_declare_prefix(const _z_session_rc_t *zs, _z_declared_keyexpr_t *out, const _z_keyexpr_t *keyexpr, size_t prefix_len) { assert(prefix_len <= _z_string_len(&keyexpr->_keyexpr)); *out = _z_declared_keyexpr_null(); _Z_RETURN_IF_ERR(_z_string_copy(&out->_inner._keyexpr, &keyexpr->_keyexpr)); if (prefix_len == 0) { return _Z_RES_OK; } #if Z_FEATURE_MULTICAST_DECLARATIONS == 0 if (_Z_RC_IN_VAL(zs)->_tp._type == _Z_TRANSPORT_MULTICAST_TYPE) { // Skip declaration since declaring a keyexpr without Z_FEATURE_MULTICAST_DECLARATIONS might generate unknown // key expression errors. return _Z_RES_OK; } #endif _z_keyexpr_wire_declaration_t declaration = _z_keyexpr_wire_declaration_null(); out->_declaration = _z_keyexpr_wire_declaration_rc_new_from_val(&declaration); if (_Z_RC_IS_NULL(&out->_declaration)) { _z_declared_keyexpr_clear(out); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } _z_string_t prefix = _z_string_alias_substr(_z_string_data(&out->_inner._keyexpr), prefix_len); _Z_CLEAN_RETURN_IF_ERR(_z_keyexpr_wire_declaration_new(_Z_RC_IN_VAL(&out->_declaration), &prefix, zs), _z_declared_keyexpr_clear(out)); return _Z_RES_OK; } z_result_t _z_declared_keyexpr_declare(const _z_session_rc_t *zs, _z_declared_keyexpr_t *out, const _z_declared_keyexpr_t *keyexpr) { if (_z_declared_keyexpr_is_fully_optimized(keyexpr, _Z_RC_IN_VAL(zs))) { return _z_declared_keyexpr_copy(out, keyexpr); } else { return _z_keyexpr_declare_prefix(zs, out, &keyexpr->_inner, _z_string_len(&keyexpr->_inner._keyexpr)); } } size_t _z_keyexpr_non_wild_prefix_len(const _z_keyexpr_t *key) { const char *data = _z_string_data(&key->_keyexpr); size_t len = _z_string_len(&key->_keyexpr); char *pos = (char *)memchr(data, '*', len); if (pos == NULL) { return len; } while (pos != data && *pos != '/') { pos--; } return _z_ptr_char_diff(pos, data); } z_result_t _z_declared_keyexpr_declare_non_wild_prefix(const _z_session_rc_t *zs, _z_declared_keyexpr_t *out, const _z_declared_keyexpr_t *keyexpr) { if (_z_declared_keyexpr_is_non_wild_prefix_optimized(keyexpr, _Z_RC_IN_VAL(zs))) { return _z_declared_keyexpr_copy(out, keyexpr); } else { return _z_keyexpr_declare_prefix(zs, out, &keyexpr->_inner, _z_keyexpr_non_wild_prefix_len(&keyexpr->_inner)); } } #define _ZP_KE_MATCH_TEMPLATE_INTERSECTS 1 #include "zenoh-pico/session/keyexpr_match_template.h" #define _ZP_KE_MATCH_TEMPLATE_INTERSECTS 0 #include "zenoh-pico/session/keyexpr_match_template.h" bool _z_keyexpr_intersects(const _z_keyexpr_t *left, const _z_keyexpr_t *right) { size_t left_len = _z_string_len(&left->_keyexpr); size_t right_len = _z_string_len(&right->_keyexpr); const char *left_start = _z_string_data(&left->_keyexpr); const char *right_start = _z_string_data(&right->_keyexpr); // fast path for identical key expressions, do we really need it ? if ((left_len == right_len) && (strncmp(left_start, right_start, left_len) == 0)) { return true; } return _z_keyexpr_forward_intersects(left_start, left_start + left_len, right_start, right_start + right_len, true); } bool _z_keyexpr_includes(const _z_keyexpr_t *left, const _z_keyexpr_t *right) { size_t left_len = _z_string_len(&left->_keyexpr); size_t right_len = _z_string_len(&right->_keyexpr); const char *left_start = _z_string_data(&left->_keyexpr); const char *right_start = _z_string_data(&right->_keyexpr); // fast path for identical key expressions, do we really need it ? if ((left_len == right_len) && (strncmp(left_start, right_start, left_len) == 0)) { return true; } return _z_keyexpr_forward_includes(left_start, left_start + left_len, right_start, right_start + right_len, true); } ================================================ FILE: src/session/liveliness.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/liveliness.h" #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/reply.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_LIVELINESS == 1 /**************** Liveliness Subscriber ****************/ #if Z_FEATURE_SUBSCRIPTION == 1 z_result_t _z_liveliness_process_remote_token_declare(_z_session_t *zn, uint32_t id, const _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer) { _z_keyexpr_t ke; _Z_RETURN_IF_ERR(_z_get_keyexpr_from_wireexpr(zn, &ke, wireexpr, peer, false)); z_result_t ret = _Z_RES_OK; _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&ke)); const _z_keyexpr_t *pkeyexpr = _z_keyexpr_intmap_get(&zn->_remote_tokens, id); if (pkeyexpr != NULL) { // Already received this token _Z_DEBUG("Duplicate token id %i", (int)id); ret = _z_keyexpr_equals(pkeyexpr, &ke) ? _Z_RES_OK : _Z_ERR_KEYEXPR_NOT_MATCH; _z_session_mutex_unlock(zn); _z_keyexpr_clear(&ke); return ret; } else { _z_keyexpr_t *ke_on_heap = (_z_keyexpr_t *)z_malloc(sizeof(_z_keyexpr_t)); if (ke_on_heap == NULL || _z_keyexpr_intmap_insert(&zn->_remote_tokens, id, ke_on_heap) == NULL) { ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; z_free(ke_on_heap); } else { *ke_on_heap = _z_keyexpr_steal(&ke); } } _z_session_mutex_unlock(zn); _z_wireexpr_t wireexpr2 = _z_wireexpr_alias(wireexpr); _Z_SET_IF_OK(ret, _z_trigger_liveliness_subscriptions_declare(zn, &wireexpr2, timestamp, peer)); if (ret != _Z_RES_OK) { _z_session_mutex_lock(zn); // remote tokens kes do not reference any declaration, so it is safe to remove them under session mutex _z_keyexpr_intmap_remove(&zn->_remote_tokens, id); _z_session_mutex_unlock(zn); _z_keyexpr_clear(&ke); } return ret; } z_result_t _z_liveliness_process_remote_token_undeclare(_z_session_t *zn, uint32_t id, const _z_timestamp_t *timestamp) { z_result_t ret = _Z_RES_OK; _z_keyexpr_t key = _z_keyexpr_null(); _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_keyexpr_t *keyexpr = (_z_keyexpr_t *)_z_keyexpr_intmap_get(&zn->_remote_tokens, id); if (keyexpr != NULL) { key = _z_keyexpr_steal(keyexpr); _z_keyexpr_intmap_remove(&zn->_remote_tokens, id); } else { _Z_ERROR_LOG(_Z_ERR_ENTITY_UNKNOWN); ret = _Z_ERR_ENTITY_UNKNOWN; } _z_session_mutex_unlock(zn); if (_z_keyexpr_check(&key)) { ret = _z_trigger_liveliness_subscriptions_undeclare(zn, &key, timestamp); _z_keyexpr_clear(&key); } return ret; } z_result_t _z_liveliness_subscription_undeclare_all(_z_session_t *zn) { z_result_t ret = _Z_RES_OK; _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); // NOTE: it is safe to just move the data, since remote tokens store full copies of ke. _z_keyexpr_intmap_t token_list = zn->_remote_tokens; _z_keyexpr_intmap_init(&zn->_remote_tokens); _z_session_mutex_unlock(zn); _z_keyexpr_intmap_iterator_t iter = _z_keyexpr_intmap_iterator_make(&token_list); _z_timestamp_t tm = _z_timestamp_null(); while (_z_keyexpr_intmap_iterator_next(&iter)) { _z_keyexpr_t *key = _z_keyexpr_intmap_iterator_value(&iter); ret = _z_trigger_liveliness_subscriptions_undeclare(zn, key, &tm); if (ret != _Z_RES_OK) { break; } } _z_keyexpr_intmap_clear(&token_list); return ret; } #endif // Z_FEATURE_SUBSCRIPTION == 1 /**************** Liveliness Query ****************/ #if Z_FEATURE_QUERY == 1 void _z_liveliness_pending_query_clear(_z_liveliness_pending_query_t *pen_qry) { if (pen_qry->_dropper != NULL) { pen_qry->_dropper(pen_qry->_arg); pen_qry->_dropper = NULL; } _z_keyexpr_clear(&pen_qry->_key); #ifdef Z_FEATURE_UNSTABLE_API _z_pending_query_cancellation_data_clear(&pen_qry->_cancellation_data); #endif } _z_liveliness_pending_query_t *_z_unsafe_liveliness_register_pending_query(_z_session_t *zn) { _z_liveliness_pending_query_t *q = (_z_liveliness_pending_query_t *)z_malloc(sizeof(_z_liveliness_pending_query_t)); if (q == NULL) { return NULL; } uint32_t id = zn->_liveliness_query_id++; q->_id = id; if (_z_liveliness_pending_query_intmap_insert(&zn->_liveliness_pending_queries, id, q) == NULL) { z_free(q); return NULL; } return q; } static z_result_t _z_liveliness_pending_query_reply(_z_session_t *zn, uint32_t interest_id, const _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer) { _Z_DEBUG("Resolving %d - %.*s on mapping 0x%x", wireexpr->_id, (int)_z_string_len(&wireexpr->_suffix), _z_string_data(&wireexpr->_suffix), (unsigned int)wireexpr->_mapping); _z_keyexpr_t ke; _Z_RETURN_IF_ERR(_z_get_keyexpr_from_wireexpr(zn, &ke, wireexpr, peer, true)); z_result_t ret = _Z_RES_OK; _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&ke)); const _z_liveliness_pending_query_t *pq = _z_liveliness_pending_query_intmap_get(&zn->_liveliness_pending_queries, interest_id); if (pq == NULL) { _Z_ERROR_LOG(_Z_ERR_ENTITY_UNKNOWN); ret = _Z_ERR_ENTITY_UNKNOWN; } _Z_DEBUG("Liveliness pending query reply %i resolve result %i", (int)interest_id, ret); if (ret == _Z_RES_OK) { _Z_DEBUG("Reply liveliness query for %.*s", (int)_z_string_len(&ke._keyexpr), _z_string_data(&ke._keyexpr)); if (!_z_keyexpr_intersects(&pq->_key, &ke)) { _Z_ERROR_LOG(_Z_ERR_QUERY_NOT_MATCH); ret = _Z_ERR_QUERY_NOT_MATCH; } if (ret == _Z_RES_OK) { _z_encoding_t encoding = _z_encoding_null(); _z_bytes_t payload = _z_bytes_null(); _z_bytes_t attachment = _z_bytes_null(); _z_source_info_t source_info = _z_source_info_null(); _z_reply_t reply; _z_reply_steal_data(&reply, &ke, _z_entity_global_id_null(), &payload, timestamp, &encoding, Z_SAMPLE_KIND_PUT, &attachment, &source_info); pq->_callback(&reply, pq->_arg); _z_reply_clear(&reply); } } _z_session_mutex_unlock(zn); _z_keyexpr_clear(&ke); return ret; } z_result_t _z_liveliness_unregister_pending_query(_z_session_t *zn, uint32_t id) { z_result_t ret = _Z_ERR_ENTITY_UNKNOWN; _z_session_mutex_lock(zn); _z_liveliness_pending_query_t *pq = _z_liveliness_pending_query_intmap_extract(&zn->_liveliness_pending_queries, id); if (pq != NULL) { _z_liveliness_pending_query_clear(pq); z_free(pq); ret = _Z_RES_OK; } _z_session_mutex_unlock(zn); return ret; } #endif // Z_FEATURE_QUERY == 1 /**************** Interest processing ****************/ z_result_t _z_liveliness_process_token_declare(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { #if Z_FEATURE_QUERY == 1 if (decl->_interest_id.has_value) { _z_liveliness_pending_query_reply(zn, decl->_interest_id.value, &decl->_decl._body._decl_token._keyexpr, &decl->_ext_timestamp, peer); } #endif #if Z_FEATURE_SUBSCRIPTION == 1 return _z_liveliness_process_remote_token_declare( zn, decl->_decl._body._decl_token._id, &decl->_decl._body._decl_token._keyexpr, &decl->_ext_timestamp, peer); #else _ZP_UNUSED(zn); _ZP_UNUSED(decl); _ZP_UNUSED(peer); return _Z_RES_OK; #endif } z_result_t _z_liveliness_process_token_undeclare(_z_session_t *zn, const _z_n_msg_declare_t *decl) { #if Z_FEATURE_SUBSCRIPTION == 1 return _z_liveliness_process_remote_token_undeclare(zn, decl->_decl._body._undecl_token._id, &decl->_ext_timestamp); #else _ZP_UNUSED(zn); _ZP_UNUSED(decl); return _Z_RES_OK; #endif } z_result_t _z_liveliness_process_declare_final(_z_session_t *zn, const _z_n_msg_declare_t *decl) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_QUERY == 1 if (decl->_interest_id.has_value) { ret = _z_liveliness_unregister_pending_query(zn, decl->_interest_id.value); if (ret != _Z_RES_OK) { _Z_ERROR_LOG(ret); } else { _Z_DEBUG("Liveliness pending query drop %zu", (size_t)decl->_interest_id.value); } } #else _ZP_UNUSED(zn); _ZP_UNUSED(decl); #endif return _Z_RES_OK; } /**************** Init/Clear ****************/ void _z_liveliness_init(_z_session_t *zn) { _z_session_mutex_lock(zn); zn->_remote_tokens = _z_keyexpr_intmap_make(); zn->_local_tokens = _z_declared_keyexpr_intmap_make(); #if Z_FEATURE_QUERY == 1 zn->_liveliness_query_id = 1; zn->_liveliness_pending_queries = _z_liveliness_pending_query_intmap_make(); #endif _z_session_mutex_unlock(zn); } void _z_liveliness_clear(_z_session_t *zn) { _z_session_mutex_lock(zn); #if Z_FEATURE_QUERY == 1 _z_liveliness_pending_query_intmap_clear(&zn->_liveliness_pending_queries); #endif _z_declared_keyexpr_intmap_t local_tokens = zn->_local_tokens; zn->_local_tokens = _z_declared_keyexpr_intmap_make(); _z_keyexpr_intmap_t remote_tokens = zn->_remote_tokens; zn->_remote_tokens = _z_keyexpr_intmap_make(); _z_session_mutex_unlock(zn); // drop maps outside of session mutex to avoid deadlock _z_declared_keyexpr_intmap_clear(&local_tokens); _z_keyexpr_intmap_clear(&remote_tokens); } #else // Z_FEATURE_LIVELINESS == 0 z_result_t _z_liveliness_process_token_declare(_z_session_t *zn, const _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(decl); _ZP_UNUSED(peer); return _Z_RES_OK; } z_result_t _z_liveliness_process_token_undeclare(_z_session_t *zn, const _z_n_msg_declare_t *decl) { _ZP_UNUSED(zn); _ZP_UNUSED(decl); return _Z_RES_OK; } z_result_t _z_liveliness_process_declare_final(_z_session_t *zn, const _z_n_msg_declare_t *decl) { _ZP_UNUSED(zn); _ZP_UNUSED(decl); return _Z_RES_OK; } #endif // Z_FEATURE_LIVELINESS == 1 ================================================ FILE: src/session/loopback.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/loopback.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/config.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/reply.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/utils/locality.h" #if defined(Z_TEST_HOOKS) static _z_session_transport_override_fn _z_transport_common_override = NULL; void _z_session_set_transport_common_override(_z_session_transport_override_fn fn) { _z_transport_common_override = fn; } #endif #if Z_FEATURE_SUBSCRIPTION == 1 || Z_FEATURE_QUERYABLE == 1 static _z_transport_common_t *_z_session_get_transport_common(_z_session_t *zn) { #if defined(Z_TEST_HOOKS) if (_z_transport_common_override != NULL) { _z_transport_common_t *override = _z_transport_common_override(zn); if (override != NULL) { return override; } } #endif switch (zn->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: return &zn->_tp._transport._unicast._common; case _Z_TRANSPORT_MULTICAST_TYPE: return &zn->_tp._transport._multicast._common; case _Z_TRANSPORT_RAWETH_TYPE: return &zn->_tp._transport._raweth._common; default: break; } return NULL; } #else static _z_transport_common_t *_z_session_get_transport_common(_z_session_t *zn) { _ZP_UNUSED(zn); return NULL; } #endif // Z_FEATURE_SUBSCRIPTION == 1 || Z_FEATURE_QUERYABLE == 1 #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_LOCAL_SUBSCRIBER == 1 z_result_t _z_session_deliver_push_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info) { _z_transport_common_t *transport = _z_session_get_transport_common(zn); if (transport == NULL) { return _Z_ERR_INVALID; } _z_wireexpr_t wireexpr = _z_keyexpr_alias_to_wire(keyexpr); _z_bytes_t payload2 = payload == NULL ? _z_bytes_null() : _z_bytes_steal(payload); _z_bytes_t attachment2 = attachment == NULL ? _z_bytes_null() : _z_bytes_steal(attachment); _z_encoding_t encoding2 = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); _z_network_message_t msg; switch (kind) { case Z_SAMPLE_KIND_PUT: { _z_n_msg_make_push_put(&msg, &wireexpr, &payload2, &encoding2, qos, timestamp, &attachment2, reliability, source_info); break; } case Z_SAMPLE_KIND_DELETE: { _z_n_msg_make_push_del(&msg, &wireexpr, qos, timestamp, reliability, source_info); break; } default: return _Z_ERR_INVALID; } return _z_handle_network_message(transport, &msg, NULL); } #else z_result_t _z_session_deliver_push_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, z_reliability_t reliability, const _z_source_info_t *source_info) { _ZP_UNUSED(zn); _ZP_UNUSED(keyexpr); _ZP_UNUSED(payload); _ZP_UNUSED(encoding); _ZP_UNUSED(kind); _ZP_UNUSED(qos); _ZP_UNUSED(timestamp); _ZP_UNUSED(attachment); _ZP_UNUSED(reliability); _ZP_UNUSED(source_info); return _Z_RES_OK; } #endif // Z_FEATURE_SUBSCRIPTION == 1 #if Z_FEATURE_QUERYABLE == 1 && Z_FEATURE_LOCAL_QUERYABLE == 1 z_result_t _z_session_deliver_query_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_slice_t *parameters, z_consolidation_mode_t consolidation, _z_bytes_t *payload, _z_encoding_t *encoding, _z_bytes_t *attachment, const _z_source_info_t *source_info, _z_zint_t qid, uint64_t timeout_ms, _z_n_qos_t qos, bool implicit_anyke) { _z_transport_common_t *transport = _z_session_get_transport_common(zn); if (transport == NULL) { return _Z_ERR_INVALID; } _z_wireexpr_t wireexpr = _z_keyexpr_alias_to_wire(keyexpr); _z_slice_t parameters2 = parameters == NULL ? _z_slice_null() : _z_slice_alias(*parameters); _z_bytes_t payload2 = payload == NULL ? _z_bytes_null() : _z_bytes_steal(payload); _z_bytes_t attachment2 = attachment == NULL ? _z_bytes_null() : _z_bytes_steal(attachment); _z_encoding_t encoding2 = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); _z_zenoh_message_t msg; _z_n_msg_make_query(&msg, &wireexpr, ¶meters2, qid, Z_RELIABILITY_DEFAULT, consolidation, &payload2, &encoding2, timeout_ms, &attachment2, qos, source_info, implicit_anyke); return _z_handle_network_message(transport, &msg, NULL); } #else z_result_t _z_session_deliver_query_locally(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_slice_t *parameters, z_consolidation_mode_t consolidation, _z_bytes_t *payload, _z_encoding_t *encoding, _z_bytes_t *attachment, const _z_source_info_t *source_info, _z_zint_t qid, uint64_t timeout_ms, _z_n_qos_t qos, bool implicit_anyke) { _ZP_UNUSED(zn); _ZP_UNUSED(keyexpr); _ZP_UNUSED(parameters); _ZP_UNUSED(consolidation); _ZP_UNUSED(payload); _ZP_UNUSED(encoding); _ZP_UNUSED(attachment); _ZP_UNUSED(source_info); _ZP_UNUSED(qid); _ZP_UNUSED(timeout_ms); _ZP_UNUSED(qos); _ZP_UNUSED(implicit_anyke); return _Z_RES_OK; } #endif // Z_FEATURE_QUERYABLE == 1 #if Z_FEATURE_QUERY == 1 z_result_t _z_session_deliver_reply_locally(const _z_query_t *query, const _z_session_rc_t *zn, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, const _z_source_info_t *source_info) { _z_transport_common_t *transport = _z_session_get_transport_common(_Z_RC_IN_VAL(zn)); if (transport == NULL) { return _Z_ERR_INVALID; } _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(keyexpr, _Z_RC_IN_VAL(zn)); _z_bytes_t payload2 = payload == NULL ? _z_bytes_null() : _z_bytes_steal(payload); _z_bytes_t attachment2 = attachment == NULL ? _z_bytes_null() : _z_bytes_steal(attachment); _z_encoding_t encoding2 = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); _z_network_message_t msg; switch (kind) { case Z_SAMPLE_KIND_PUT: _z_n_msg_make_reply_ok_put(&msg, &_Z_RC_IN_VAL(zn)->_local_zid, query->_request_id, &wireexpr, Z_RELIABILITY_DEFAULT, Z_CONSOLIDATION_MODE_DEFAULT, qos, timestamp, source_info, &payload2, &encoding2, &attachment2); break; case Z_SAMPLE_KIND_DELETE: _z_n_msg_make_reply_ok_del(&msg, &_Z_RC_IN_VAL(zn)->_local_zid, query->_request_id, &wireexpr, Z_RELIABILITY_DEFAULT, Z_CONSOLIDATION_MODE_DEFAULT, qos, timestamp, source_info, &attachment2); break; default: return _Z_ERR_INVALID; } return _z_handle_network_message(transport, &msg, NULL); } z_result_t _z_session_deliver_reply_err_locally(const _z_query_t *query, const _z_session_rc_t *zn, _z_bytes_t *payload, _z_encoding_t *encoding, _z_n_qos_t qos) { _z_transport_common_t *transport = _z_session_get_transport_common(_Z_RC_IN_VAL(zn)); if (transport == NULL) { return _Z_ERR_INVALID; } _z_bytes_t payload2 = payload == NULL ? _z_bytes_null() : _z_bytes_steal(payload); _z_encoding_t encoding2 = encoding == NULL ? _z_encoding_null() : _z_encoding_steal(encoding); _z_network_message_t msg; _z_n_msg_make_reply_err(&msg, &_Z_RC_IN_VAL(zn)->_local_zid, query->_request_id, Z_RELIABILITY_DEFAULT, qos, &payload2, &encoding2, NULL); return _z_handle_network_message(transport, &msg, NULL); } z_result_t _z_session_deliver_reply_final_locally(_z_session_t *zn, _z_zint_t rid) { if (zn == NULL) { return _Z_ERR_INVALID; } return _z_trigger_query_reply_final(zn, rid); } #else z_result_t _z_session_deliver_reply_locally(const _z_query_t *query, const _z_session_rc_t *responder, const _z_declared_keyexpr_t *keyexpr, _z_bytes_t *payload, _z_encoding_t *encoding, z_sample_kind_t kind, _z_n_qos_t qos, const _z_timestamp_t *timestamp, _z_bytes_t *attachment, const _z_source_info_t *source_info) { _ZP_UNUSED(query); _ZP_UNUSED(responder); _ZP_UNUSED(keyexpr); _ZP_UNUSED(payload); _ZP_UNUSED(encoding); _ZP_UNUSED(kind); _ZP_UNUSED(qos); _ZP_UNUSED(timestamp); _ZP_UNUSED(attachment); _ZP_UNUSED(source_info); return _Z_RES_OK; } z_result_t _z_session_deliver_reply_err_locally(const _z_query_t *query, const _z_session_rc_t *zn, _z_bytes_t *payload, _z_encoding_t *encoding, _z_n_qos_t qos) { _ZP_UNUSED(query); _ZP_UNUSED(zn); _ZP_UNUSED(payload); _ZP_UNUSED(encoding); _ZP_UNUSED(qos); return _Z_RES_OK; } z_result_t _z_session_deliver_reply_final_locally(_z_session_t *zn, _z_zint_t rid) { _ZP_UNUSED(zn); _ZP_UNUSED(rid); return _Z_RES_OK; } #endif // Z_FEATURE_QUERY == 1 ================================================ FILE: src/session/push.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/push.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/config.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_SUBSCRIPTION == 1 z_result_t _z_trigger_push(_z_session_t *zn, _z_n_msg_push_t *push, z_reliability_t reliability, _z_transport_peer_common_t *peer) { z_result_t ret = _Z_RES_OK; // Memory cleaning must be done in the feature layer if (push->_body._is_put) { _z_msg_put_t *put = &push->_body._body._put; ret = _z_trigger_subscriptions_put(zn, &push->_key, &put->_payload, &put->_encoding, &put->_commons._timestamp, push->_qos, &put->_attachment, reliability, &put->_commons._source_info, peer); } else { _z_msg_del_t *del = &push->_body._body._del; ret = _z_trigger_subscriptions_del(zn, &push->_key, &del->_commons._timestamp, push->_qos, &del->_attachment, reliability, &del->_commons._source_info, peer); } return ret; } #else z_result_t _z_trigger_push(_z_session_t *zn, _z_n_msg_push_t *push, z_reliability_t reliability, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(push); _ZP_UNUSED(reliability); _ZP_UNUSED(peer); return _Z_RES_OK; } #endif ================================================ FILE: src/session/query.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/query.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/net/reply.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_QUERY == 1 void _z_pending_query_clear(_z_pending_query_t *pen_qry) { if (pen_qry->_dropper != NULL) { pen_qry->_dropper(pen_qry->_arg); pen_qry->_dropper = NULL; } _z_keyexpr_clear(&pen_qry->_key); _z_pending_reply_slist_free(&pen_qry->_pending_replies); pen_qry->_allowed_destination = z_locality_default(); pen_qry->_remaining_finals = 0; #ifdef Z_FEATURE_UNSTABLE_API _z_pending_query_cancellation_data_clear(&pen_qry->_cancellation_data); #endif } bool _z_pending_query_eq(const _z_pending_query_t *one, const _z_pending_query_t *two) { return one->_id == two->_id; } bool _z_pending_query_querier_eq(const _z_pending_query_t *one, const _z_pending_query_t *two) { return one->_querier_id.has_value == two->_querier_id.has_value && one->_querier_id.value == two->_querier_id.value; } static bool _z_pending_query_timeout(const _z_pending_query_t *foo, const _z_pending_query_t *pq) { _ZP_UNUSED(foo); bool result = z_clock_elapsed_ms((z_clock_t *)&pq->_start_time) >= pq->_timeout; if (result) { _Z_INFO("Dropping query because of timeout"); } return result; } void _z_pending_query_process_timeout(_z_session_t *zn) { _z_session_mutex_lock(zn); // Extract all queries with timeout elapsed zn->_pending_queries = _z_pending_query_slist_drop_all_filter(zn->_pending_queries, _z_pending_query_timeout, NULL); _z_session_mutex_unlock(zn); } _z_fut_fn_result_t _z_pending_query_process_timeout_task_fn(void *session_arg, _z_executor_t *executor) { _ZP_UNUSED(executor); _z_session_t *zn = (_z_session_t *)session_arg; _z_pending_query_process_timeout(zn); return _z_fut_fn_result_wake_up_after(1000); } /*------------------ Query ------------------*/ /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ _z_pending_query_t *_z_unsafe_get_pending_query_by_id(_z_session_t *zn, const _z_zint_t id) { _z_pending_query_t *ret = NULL; _z_pending_query_slist_t *xs = zn->_pending_queries; while (xs != NULL) { _z_pending_query_t *pql = _z_pending_query_slist_value(xs); if (pql->_id == id) { ret = pql; break; } xs = _z_pending_query_slist_next(xs); } return ret; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ _z_pending_query_t *_z_unsafe_register_pending_query(_z_session_t *zn) { _z_zint_t qid = zn->_query_id++; zn->_pending_queries = _z_pending_query_slist_push_empty(zn->_pending_queries); _z_pending_query_t *pq = _z_pending_query_slist_value(zn->_pending_queries); pq->_id = qid; return pq; } static z_result_t _z_trigger_query_reply_partial_inner(_z_session_t *zn, const _z_zint_t id, _z_keyexpr_t *keyexpr, _z_msg_put_t *msg, z_sample_kind_t kind, _z_entity_global_id_t *replier_id) { _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(keyexpr); _z_msg_put_clear(msg)); // Get query infos _z_pending_query_t *pen_qry = _z_unsafe_get_pending_query_by_id(zn, id); if (pen_qry == NULL) { _z_session_mutex_unlock(zn); _z_keyexpr_clear(keyexpr); _z_msg_put_clear(msg); // Not concerned by the reply return _Z_RES_OK; } if (!pen_qry->_anyke && !_z_keyexpr_intersects(&pen_qry->_key, keyexpr)) { _z_session_mutex_unlock(zn); _z_keyexpr_clear(keyexpr); _z_msg_put_clear(msg); // Not concerned by the reply return _Z_RES_OK; } // Build the reply _z_reply_t reply; _z_reply_steal_data(&reply, keyexpr, *replier_id, &msg->_payload, &msg->_commons._timestamp, &msg->_encoding, kind, &msg->_attachment, &msg->_commons._source_info); // Process monotonic & latest consolidation mode if ((pen_qry->_consolidation == Z_CONSOLIDATION_MODE_LATEST) || (pen_qry->_consolidation == Z_CONSOLIDATION_MODE_MONOTONIC)) { bool drop = false; _z_pending_reply_slist_t *curr_node = pen_qry->_pending_replies; _z_pending_reply_t *pen_rep = NULL; // Verify if this is a newer reply, free the old one in case it is while (curr_node != NULL) { pen_rep = _z_pending_reply_slist_value(curr_node); // Check if this is the same resource key if (_z_declared_keyexpr_equals(&pen_rep->_reply.data._result.sample.keyexpr, &reply.data._result.sample.keyexpr)) { if (msg->_commons._timestamp.time <= pen_rep->_tstamp.time) { drop = true; } else { pen_qry->_pending_replies = _z_pending_reply_slist_drop_first_filter(pen_qry->_pending_replies, _z_pending_reply_eq, pen_rep); } break; } curr_node = _z_pending_reply_slist_next(curr_node); } if (!drop) { // Cache most recent reply _z_pending_reply_t tmp_rep; if (pen_qry->_consolidation == Z_CONSOLIDATION_MODE_MONOTONIC) { // No need to store the whole reply in the monotonic mode. tmp_rep._reply = _z_reply_null(); tmp_rep._reply.data._tag = _Z_REPLY_TAG_DATA; _Z_CLEAN_RETURN_IF_ERR(_z_declared_keyexpr_copy(&tmp_rep._reply.data._result.sample.keyexpr, &reply.data._result.sample.keyexpr), _z_reply_clear(&reply); _z_session_mutex_unlock(zn)); } else { // Copy the reply to store it out of context _Z_CLEAN_RETURN_IF_ERR(_z_reply_move(&tmp_rep._reply, &reply), _z_reply_clear(&reply); _z_session_mutex_unlock(zn)); } tmp_rep._tstamp = _z_timestamp_duplicate(&msg->_commons._timestamp); pen_qry->_pending_replies = _z_pending_reply_slist_push(pen_qry->_pending_replies, &tmp_rep); _Z_DEBUG("stored reply for id=%jd consolidation=%d", (intmax_t)id, pen_qry->_consolidation); } } _z_session_mutex_unlock(zn); // Trigger callback if applicable if (pen_qry->_consolidation != Z_CONSOLIDATION_MODE_LATEST) { _Z_DEBUG("immediate callback for id=%jd", (intmax_t)id); pen_qry->_callback(&reply, pen_qry->_arg); } _z_reply_clear(&reply); return _Z_RES_OK; } z_result_t _z_trigger_query_reply_partial(_z_session_t *zn, const _z_zint_t id, _z_wireexpr_t *wireexpr, _z_msg_put_t *msg, z_sample_kind_t kind, _z_entity_global_id_t *replier_id, _z_transport_peer_common_t *peer) { _z_keyexpr_t keyexpr; z_result_t ret = _z_get_keyexpr_from_wireexpr(zn, &keyexpr, wireexpr, peer, true); _Z_SET_IF_OK(ret, _z_trigger_query_reply_partial_inner(zn, id, &keyexpr, msg, kind, replier_id)); // Clean up _z_keyexpr_clear(&keyexpr); _z_wireexpr_clear(wireexpr); _z_msg_put_clear(msg); return ret; } z_result_t _z_trigger_query_reply_err(_z_session_t *zn, _z_zint_t id, _z_msg_err_t *msg, _z_entity_global_id_t *replier_id) { // Retrieve query _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_bytes_drop(&msg->_payload); _z_encoding_clear(&msg->_encoding)); _z_pending_query_t *pen_qry = _z_unsafe_get_pending_query_by_id(zn, id); _z_session_mutex_unlock(zn); if (pen_qry == NULL) { // Not concerned by the reply _z_bytes_drop(&msg->_payload); _z_encoding_clear(&msg->_encoding); return _Z_RES_OK; } // Trigger the user callback _z_reply_t reply; _z_reply_err_steal_data(&reply, &msg->_payload, &msg->_encoding, *replier_id); pen_qry->_callback(&reply, pen_qry->_arg); _z_reply_clear(&reply); return _Z_RES_OK; } z_result_t _z_trigger_query_reply_final(_z_session_t *zn, _z_zint_t id) { // Retrieve query _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); _z_pending_query_t *pen_qry = _z_unsafe_get_pending_query_by_id(zn, id); if (pen_qry == NULL) { _z_session_mutex_unlock(zn); // Not concerned by the reply return _Z_RES_OK; } _Z_DEBUG("trigger_reply_final id=%jd", (intmax_t)id); if (pen_qry->_remaining_finals > 0) { pen_qry->_remaining_finals--; } bool do_finalize = (pen_qry->_remaining_finals == 0); if (pen_qry->_consolidation == Z_CONSOLIDATION_MODE_LATEST && do_finalize) { while (pen_qry->_pending_replies != NULL) { _z_pending_reply_t *pen_rep = _z_pending_reply_slist_value(pen_qry->_pending_replies); // Trigger the query handler _Z_DEBUG("deliver pending reply in final id=%jd", (intmax_t)id); pen_qry->_callback(&pen_rep->_reply, pen_qry->_arg); pen_qry->_pending_replies = _z_pending_reply_slist_pop(pen_qry->_pending_replies); } } // Finalize query if requested: drop pending query and trigger dropper callback, // which is equivalent to a reply with FINAL. if (do_finalize) { zn->_pending_queries = _z_pending_query_slist_drop_first_filter(zn->_pending_queries, _z_pending_query_eq, pen_qry); } _z_session_mutex_unlock(zn); return _Z_RES_OK; } void _z_unregister_pending_query(_z_session_t *zn, _z_zint_t qid) { _z_pending_query_t target = {0}; target._id = qid; _z_session_mutex_lock(zn); zn->_pending_queries = _z_pending_query_slist_drop_first_filter(zn->_pending_queries, _z_pending_query_eq, &target); _z_session_mutex_unlock(zn); } void _z_unregister_pending_queries_from_querier(_z_session_t *zn, uint32_t querier_id) { _z_pending_query_t target = {0}; target._querier_id = _z_optional_id_make_some(querier_id); _z_session_mutex_lock(zn); zn->_pending_queries = _z_pending_query_slist_drop_all_filter(zn->_pending_queries, _z_pending_query_querier_eq, &target); _z_session_mutex_unlock(zn); } void _z_flush_pending_queries(_z_session_t *zn) { _z_session_mutex_lock(zn); _z_pending_query_slist_t *queries = zn->_pending_queries; zn->_pending_queries = _z_pending_query_slist_new(); _z_session_mutex_unlock(zn); _z_pending_query_slist_free(&queries); } #ifdef Z_FEATURE_UNSTABLE_API typedef struct _z_cancel_pending_query_arg_t { _z_session_weak_t _zn; _z_zint_t _qid; } _z_cancel_pending_query_arg_t; z_result_t _z_cancel_pending_query(void *arg) { _z_cancel_pending_query_arg_t *a = (_z_cancel_pending_query_arg_t *)arg; _z_session_rc_t s_rc = _z_session_weak_upgrade_if_open(&a->_zn); if (!_Z_RC_IS_NULL(&s_rc)) { _z_unregister_pending_query(_Z_RC_IN_VAL(&s_rc), a->_qid); } _z_session_rc_drop(&s_rc); return _Z_RES_OK; } void _z_cancel_pending_query_arg_drop(void *arg) { _z_cancel_pending_query_arg_t *a = (_z_cancel_pending_query_arg_t *)arg; _z_session_weak_drop(&a->_zn); z_free(a); } z_result_t _z_pending_query_register_cancellation(_z_pending_query_t *pq, const _z_cancellation_token_rc_t *opt_cancellation_token, const _z_session_rc_t *zn) { pq->_cancellation_data = _z_pending_query_cancellation_data_null(); if (opt_cancellation_token != NULL) { _z_cancel_pending_query_arg_t *arg = (_z_cancel_pending_query_arg_t *)z_malloc(sizeof(_z_cancel_pending_query_arg_t)); if (arg == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } arg->_zn = _z_session_rc_clone_as_weak(zn); arg->_qid = pq->_id; size_t handler_id = 0; _z_cancellation_token_on_cancel_handler_t handler; handler._on_cancel = _z_cancel_pending_query; handler._on_drop = _z_cancel_pending_query_arg_drop; handler._arg = (void *)arg; _Z_CLEAN_RETURN_IF_ERR( _z_cancellation_token_add_on_cancel_handler(_Z_RC_IN_VAL(opt_cancellation_token), &handler, &handler_id), _z_cancellation_token_on_cancel_handler_drop(&handler)); pq->_cancellation_data._cancellation_token = _z_cancellation_token_rc_clone(opt_cancellation_token); pq->_cancellation_data._handler_id = handler_id; _Z_CLEAN_RETURN_IF_ERR( _z_cancellation_token_get_notifier(_Z_RC_IN_VAL(opt_cancellation_token), &pq->_cancellation_data._notifier), _z_pending_query_cancellation_data_clear(&pq->_cancellation_data)); } return _Z_RES_OK; } #endif #else void _z_pending_query_process_timeout(_z_session_t* zn) { _ZP_UNUSED(zn); return; } #endif ================================================ FILE: src/session/queryable.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/query.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/string.h" #if Z_FEATURE_QUERYABLE == 1 #define _Z_QLEINFOS_VEC_SIZE 4 // Arbitrary initial size static inline _z_queryable_cache_data_t _z_queryable_cache_data_null(void) { _z_queryable_cache_data_t ret = {0}; return ret; } void _z_unsafe_queryable_cache_invalidate(_z_session_t *zn) { #if Z_FEATURE_RX_CACHE == 1 _z_queryable_lru_cache_clear(&zn->_queryable_cache); #else _ZP_UNUSED(zn); #endif } #if Z_FEATURE_RX_CACHE == 1 int _z_queryable_cache_data_compare(const void *first, const void *second) { const _z_queryable_cache_data_t *first_data = (const _z_queryable_cache_data_t *)first; const _z_queryable_cache_data_t *second_data = (const _z_queryable_cache_data_t *)second; if (first_data->is_remote != second_data->is_remote) { return (int)first_data->is_remote - (int)second_data->is_remote; } return _z_keyexpr_compare(&first_data->ke, &second_data->ke); } #endif // Z_FEATURE_RX_CACHE == 1 void _z_queryable_cache_data_clear(_z_queryable_cache_data_t *val) { _z_session_queryable_rc_svec_rc_drop(&val->infos); _z_keyexpr_clear(&val->ke); } bool _z_session_queryable_eq(const _z_session_queryable_t *one, const _z_session_queryable_t *two) { return one->_id == two->_id; } void _z_session_queryable_clear(_z_session_queryable_t *qle) { if (qle->_dropper != NULL) { qle->_dropper(qle->_arg); qle->_dropper = NULL; } _z_declared_keyexpr_clear(&qle->_key); _z_sync_group_notifier_drop(&qle->_session_callback_drop_notifier); _z_sync_group_notifier_drop(&qle->_queryable_callback_drop_notifier); } /*------------------ Queryable ------------------*/ static _z_session_queryable_rc_t *__z_get_session_queryable_by_id(_z_session_queryable_rc_slist_t *qles, const _z_zint_t id) { _z_session_queryable_rc_t *ret = NULL; _z_session_queryable_rc_slist_t *xs = qles; while (xs != NULL) { _z_session_queryable_rc_t *qle = _z_session_queryable_rc_slist_value(xs); if (id == _Z_RC_IN_VAL(qle)->_id) { ret = qle; break; } xs = _z_session_queryable_rc_slist_next(xs); } return ret; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static _z_session_queryable_rc_t *__unsafe_z_get_session_queryable_by_id(_z_session_t *zn, const _z_zint_t id) { _z_session_queryable_rc_slist_t *qles = zn->_local_queryable; return __z_get_session_queryable_by_id(qles, id); } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static z_result_t __unsafe_z_get_session_queryables_by_key(_z_session_t *zn, const _z_keyexpr_t *key, bool is_remote, _z_session_queryable_rc_svec_t *qle_infos) { _z_session_queryable_rc_slist_t *qles = zn->_local_queryable; *qle_infos = _z_session_queryable_rc_svec_make(_Z_QLEINFOS_VEC_SIZE); _Z_RETURN_ERR_OOM_IF_TRUE(qle_infos->_val == NULL); _z_session_queryable_rc_slist_t *xs = qles; while (xs != NULL) { // Parse queryable list _z_session_queryable_rc_t *qle = _z_session_queryable_rc_slist_value(xs); const _z_session_queryable_t *qle_val = _Z_RC_IN_VAL(qle); bool origin_allowed = is_remote ? _z_locality_allows_remote(qle_val->_allowed_origin) : _z_locality_allows_local(qle_val->_allowed_origin); if (origin_allowed && _z_keyexpr_intersects(&qle_val->_key._inner, key)) { _z_session_queryable_rc_t qle_clone = _z_session_queryable_rc_clone(qle); _Z_CLEAN_RETURN_IF_ERR(_z_session_queryable_rc_svec_append(qle_infos, &qle_clone, false), _z_session_queryable_rc_svec_clear(qle_infos)); } xs = _z_session_queryable_rc_slist_next(xs); } return _Z_RES_OK; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static z_result_t __unsafe_z_get_session_queryables_rc_by_key(_z_session_t *zn, const _z_keyexpr_t *key, bool is_remote, _z_session_queryable_rc_svec_rc_t *qle_infos) { *qle_infos = _z_session_queryable_rc_svec_rc_new_undefined(); z_result_t ret = !_Z_RC_IS_NULL(qle_infos) ? _Z_RES_OK : _Z_ERR_SYSTEM_OUT_OF_MEMORY; _Z_SET_IF_OK(ret, __unsafe_z_get_session_queryables_by_key(zn, key, is_remote, _Z_RC_IN_VAL(qle_infos))); if (ret != _Z_RES_OK) { _z_session_queryable_rc_svec_rc_drop(qle_infos); } return _Z_RES_OK; } _z_session_queryable_rc_t _z_get_session_queryable_by_id(_z_session_t *zn, const _z_zint_t id) { _z_session_queryable_rc_t out = _z_session_queryable_rc_null(); _z_session_mutex_lock(zn); _z_session_queryable_rc_t *qle = __unsafe_z_get_session_queryable_by_id(zn, id); if (qle != NULL) { out = _z_session_queryable_rc_clone(qle); } _z_session_mutex_unlock(zn); return out; } _z_session_queryable_rc_t _z_register_session_queryable(_z_session_t *zn, _z_session_queryable_t *q) { _Z_DEBUG(">>> Allocating queryable for (%.*s)", (int)_z_string_len(&q->_key._inner._keyexpr), _z_string_data(&q->_key._inner._keyexpr)); _z_session_queryable_rc_t out = _z_session_queryable_rc_new_from_val(q); if (_Z_RC_IS_NULL(&out)) { return out; } if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { _Z_WARN("Failed to acquire session mutex for registering queryable - session is closed"); _z_session_queryable_rc_drop(&out); *q = _z_session_queryable_null(); return out; } _z_unsafe_queryable_cache_invalidate(zn); zn->_local_queryable = _z_session_queryable_rc_slist_push_empty(zn->_local_queryable); _z_session_queryable_rc_t *ret = _z_session_queryable_rc_slist_value(zn->_local_queryable); *ret = _z_session_queryable_rc_clone( &out); // immediately increase reference count to prevent eventual drop by concurrent session close _z_session_mutex_unlock(zn); #if Z_FEATURE_LOCAL_QUERYABLE == 1 if (!_Z_RC_IS_NULL(&out) && _z_locality_allows_local(q->_allowed_origin)) { _z_session_queryable_t *qle_val = _Z_RC_IN_VAL(&out); _z_write_filter_notify_queryable(zn, &qle_val->_key._inner, qle_val->_allowed_origin, qle_val->_complete, true); } #endif return out; } static z_result_t _z_session_queryable_get_infos(_z_session_t *zn, _z_queryable_cache_data_t *out, const _z_wireexpr_t *wireexpr, _z_transport_peer_common_t *peer) { out->is_remote = (peer != NULL); _Z_RETURN_IF_ERR(_z_get_keyexpr_from_wireexpr(zn, &out->ke, wireexpr, peer, true)); _z_queryable_cache_data_t *cache_entry = NULL; z_result_t ret = _Z_RES_OK; _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&out->ke)); #if Z_FEATURE_RX_CACHE == 1 cache_entry = _z_queryable_lru_cache_get(&zn->_queryable_cache, out); if (cache_entry != NULL && cache_entry->is_remote != out->is_remote) { cache_entry = NULL; } #endif if (cache_entry != NULL) { // Copy cache entry out->infos = _z_session_queryable_rc_svec_rc_clone(&cache_entry->infos); } else { // Build queryable data _Z_SET_IF_OK(ret, __unsafe_z_get_session_queryables_rc_by_key(zn, &out->ke, out->is_remote, &out->infos)); #if Z_FEATURE_RX_CACHE == 1 // Update cache _z_queryable_cache_data_t cache_storage = _z_queryable_cache_data_null(); cache_storage.infos = _z_session_queryable_rc_svec_rc_clone(&out->infos); cache_storage.is_remote = out->is_remote; _Z_SET_IF_OK(ret, _z_keyexpr_copy(&cache_storage.ke, &out->ke)); _Z_SET_IF_OK(ret, _z_queryable_lru_cache_insert(&zn->_queryable_cache, &cache_storage)); if (ret != _Z_RES_OK) { _z_queryable_cache_data_clear(&cache_storage); } #endif } _z_session_mutex_unlock(zn); if (ret != _Z_RES_OK) { _z_queryable_cache_data_clear(out); } return ret; } z_result_t _z_trigger_queryables(_z_transport_common_t *transport, _z_msg_query_t *msgq, _z_wireexpr_t *q_key, uint32_t qid, _z_n_qos_t qos, _z_transport_peer_common_t *peer) { bool is_local = peer == NULL; _z_session_t *zn = _z_transport_common_get_session(transport); _z_queryable_cache_data_t qle_infos = _z_queryable_cache_data_null(); // Retrieve sub infos _Z_CLEAN_RETURN_IF_ERR(_z_session_queryable_get_infos(zn, &qle_infos, q_key, peer), _z_wireexpr_clear(q_key); _z_msg_query_clear(msgq)); // Check if there are queryables const _z_session_queryable_rc_svec_t *qles = _Z_RC_IN_VAL(&qle_infos.infos); size_t qle_nb = _z_session_queryable_rc_svec_len(qles); _Z_DEBUG("Triggering %ju queryables for key %.*s", (uintmax_t)qle_nb, (int)_z_string_len(&qle_infos.ke._keyexpr), _z_string_data(&qle_infos.ke._keyexpr)); if (qle_nb == 0) { // optimization for local queries, since moves can imply extra copy if aliased _z_wireexpr_clear(q_key); _z_queryable_cache_data_clear(&qle_infos); _z_msg_query_clear(msgq); _z_session_send_reply_final(zn, qid, is_local); return _Z_RES_OK; } // Build the z_query _z_query_rc_t query = _z_query_rc_new_undefined(); z_result_t ret = _Z_RC_IS_NULL(&query) ? _Z_ERR_SYSTEM_OUT_OF_MEMORY : _Z_RES_OK; // Note: _z_query_move_data will make copies of all aliased fields, since query is under ref count // and thus it is impossible to detect when user moves it out of callback _Z_SET_IF_OK(ret, _z_query_move_data(_Z_RC_IN_VAL(&query), &msgq->_ext_value, &qle_infos.ke, &msgq->_parameters, &transport->_session, qid, &msgq->_ext_attachment, msgq->_implicit_anyke, &msgq->_ext_info)); _Z_CLEAN_RETURN_IF_ERR(ret, _z_wireexpr_clear(q_key); _z_msg_query_clear(msgq); _z_queryable_cache_data_clear(&qle_infos); _z_query_rc_drop(&query)) _Z_RC_IN_VAL(&query)->_qos = qos; _Z_RC_IN_VAL(&query)->_is_local = is_local; // Parse session_queryable svec for (size_t i = 0; i < qle_nb; i++) { _z_session_queryable_t *qle_info = _Z_RC_IN_VAL(_z_session_queryable_rc_svec_get(qles, i)); if (i + 1 == qle_nb) { qle_info->_callback(&query, qle_info->_arg); } else { _z_query_rc_t query_copy = _z_query_rc_clone(&query); qle_info->_callback(&query_copy, qle_info->_arg); _z_query_rc_drop(&query_copy); } } _z_wireexpr_clear(q_key); _z_query_rc_drop(&query); _z_queryable_cache_data_clear(&qle_infos); return _Z_RES_OK; } void _z_unregister_session_queryable(_z_session_t *zn, _z_session_queryable_rc_t *qle) { #if Z_FEATURE_LOCAL_QUERYABLE == 1 _z_session_queryable_t *qle_val = _Z_RC_IN_VAL(qle); _z_write_filter_notify_queryable(zn, &qle_val->_key._inner, qle_val->_allowed_origin, qle_val->_complete, false); #endif _z_session_mutex_lock(zn); _z_unsafe_queryable_cache_invalidate(zn); zn->_local_queryable = _z_session_queryable_rc_slist_drop_first_filter(zn->_local_queryable, _z_session_queryable_rc_eq, qle); _z_session_mutex_unlock(zn); _z_session_queryable_rc_drop(qle); } void _z_flush_session_queryable(_z_session_t *zn) { _z_session_queryable_rc_slist_t *queryables; _z_session_mutex_lock(zn); _z_unsafe_queryable_cache_invalidate(zn); queryables = zn->_local_queryable; zn->_local_queryable = _z_session_queryable_rc_slist_new(); _z_session_mutex_unlock(zn); _z_session_queryable_rc_slist_free(&queryables); } #else // Z_FEATURE_QUERYABLE == 0 void _z_unsafe_queryable_cache_invalidate(_z_session_t *zn) { _ZP_UNUSED(zn); } #endif // Z_FEATURE_QUERYABLE == 1 ================================================ FILE: src/session/reply.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/reply.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/config.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/utils/logging.h" z_result_t _z_trigger_reply_partial(_z_session_t *zn, _z_zint_t id, _z_wireexpr_t *key, _z_msg_reply_t *reply, _z_entity_global_id_t *replier_id, _z_transport_peer_common_t *peer) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_QUERY == 1 ret = _z_trigger_query_reply_partial(zn, id, key, &reply->_body._body._put, (reply->_body._is_put ? Z_SAMPLE_KIND_PUT : Z_SAMPLE_KIND_DELETE), replier_id, peer); #else _ZP_UNUSED(zn); _ZP_UNUSED(id); _ZP_UNUSED(key); _ZP_UNUSED(reply); _ZP_UNUSED(replier_id); _ZP_UNUSED(peer); #endif return ret; } z_result_t _z_trigger_reply_err(_z_session_t *zn, _z_zint_t id, _z_msg_err_t *error, _z_entity_global_id_t *replier_id) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_QUERY == 1 ret = _z_trigger_query_reply_err(zn, id, error, replier_id); #else _ZP_UNUSED(zn); _ZP_UNUSED(id); _ZP_UNUSED(error); _ZP_UNUSED(replier_id); #endif return ret; } z_result_t _z_trigger_reply_final(_z_session_t *zn, _z_n_msg_response_final_t *final) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_QUERY == 1 // TODO check id to know where to dispatch _z_zint_t id = final->_request_id; _z_trigger_query_reply_final(zn, id); #else _ZP_UNUSED(zn); _ZP_UNUSED(final); #endif return ret; } ================================================ FILE: src/session/resource.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/resource.h" #include #include #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" bool _z_resource_eq(const _z_resource_t *other, const _z_resource_t *this_) { return this_->_id == other->_id; } void _z_resource_clear(_z_resource_t *res) { _z_keyexpr_clear(&res->_key); } size_t _z_resource_size(_z_resource_t *p) { _ZP_UNUSED(p); return sizeof(_z_resource_t); } void _z_resource_copy(_z_resource_t *dst, const _z_resource_t *src) { _z_keyexpr_copy(&dst->_key, &src->_key); dst->_id = src->_id; } void _z_resource_free(_z_resource_t **res) { _z_resource_t *ptr = *res; if (ptr != NULL) { _z_resource_clear(ptr); z_free(ptr); *res = NULL; } } /*------------------ Entity ------------------*/ uint32_t _z_get_entity_id(_z_session_t *zn) { return zn->_entity_id++; } uint16_t _z_get_resource_id(_z_session_t *zn) { return zn->_resource_id++; } /*------------------ Resource ------------------*/ _z_resource_t *_z_get_resource_by_id_inner(_z_resource_slist_t *rl, const _z_zint_t id) { _z_resource_t *ret = NULL; _z_resource_slist_t *xs = rl; while (xs != NULL) { _z_resource_t *r = _z_resource_slist_value(xs); if (r->_id == id) { ret = r; break; } xs = _z_resource_slist_next(xs); } return ret; } _z_resource_t *_z_get_resource_by_key_inner(_z_resource_slist_t *rl, const _z_keyexpr_t *keyexpr) { _z_resource_t *ret = NULL; _z_resource_slist_t *xs = rl; while (xs != NULL) { _z_resource_t *r = _z_resource_slist_value(xs); if (_z_keyexpr_equals(&r->_key, keyexpr)) { ret = r; break; } xs = _z_resource_slist_next(xs); } return ret; } static z_result_t _z_get_keyexpr_from_wireexpr_inner(_z_keyexpr_t *ret, _z_resource_slist_t *xs, const _z_wireexpr_t *expr, bool alias_wireexpr_if_possible) { *ret = _z_keyexpr_null(); _z_zint_t id = expr->_id; if (id == Z_RESOURCE_ID_NONE) { // Check if ke is already expanded if (alias_wireexpr_if_possible) { ret->_keyexpr = _z_string_alias(expr->_suffix); return _Z_RES_OK; } else { return _z_string_copy(&ret->_keyexpr, &expr->_suffix); } } else { _z_resource_t *res = _z_get_resource_by_id_inner(xs, id); if (res == NULL) { return _Z_ERR_KEYEXPR_UNKNOWN; } _z_keyexpr_t ke_prefix = _z_keyexpr_alias(&res->_key); return _z_string_concat(&ret->_keyexpr, &ke_prefix._keyexpr, &expr->_suffix, NULL, 0); } } z_result_t _z_get_keyexpr_from_wireexpr(_z_session_t *zn, _z_keyexpr_t *out, const _z_wireexpr_t *expr, _z_transport_peer_common_t *peer, bool alias_wireexpr_if_possible) { *out = _z_keyexpr_null(); z_result_t ret = _Z_ERR_NULL; if (expr != NULL && _z_wireexpr_check(expr)) { _z_session_mutex_lock(zn); _z_resource_slist_t *decls = (_z_wireexpr_is_local(expr) || (peer == NULL)) ? zn->_local_resources : peer->_remote_resources; ret = _z_get_keyexpr_from_wireexpr_inner(out, decls, expr, alias_wireexpr_if_possible); _z_session_mutex_unlock(zn); } return ret; } z_result_t _z_register_resource_inner(_z_session_t *zn, const _z_wireexpr_t *expr, uint16_t id, _z_transport_peer_common_t *peer, uint16_t *out_id) { _z_resource_slist_t **resources = (peer == NULL) ? &zn->_local_resources : &peer->_remote_resources; _z_resource_slist_t *parent_resources = (expr->_mapping == _Z_KEYEXPR_MAPPING_LOCAL) ? zn->_local_resources : peer->_remote_resources; _z_keyexpr_t new_key = _z_keyexpr_null(); if (expr->_id != Z_RESOURCE_ID_NONE) { _z_resource_t *res = _z_get_resource_by_id_inner(parent_resources, expr->_id); if (res == NULL) { _Z_ERROR("Unknown scope: %d, for mapping: %zu", (unsigned int)expr->_id, (size_t)expr->_mapping); return _Z_ERR_ENTITY_DECLARATION_FAILED; } if (_z_wireexpr_has_suffix(expr)) { if (_z_string_concat(&new_key._keyexpr, &res->_key._keyexpr, &expr->_suffix, NULL, 0) != _Z_RES_OK) { _Z_ERROR("Failed to allocate memory for new string"); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } } else if (id == Z_RESOURCE_ID_NONE && *resources == parent_resources) { // declaration of already declared resource res->_refcount++; *out_id = res->_id; return _Z_RES_OK; } else { // redeclaration of exisiting resource new_key = _z_keyexpr_alias(&res->_key); } } else { new_key = _z_keyexpr_alias_from_string(&expr->_suffix); } if (id == Z_RESOURCE_ID_NONE) { _z_resource_t *res = _z_get_resource_by_key_inner(*resources, &new_key); if (res != NULL) { // declaration of already declared resource res->_refcount++; _z_keyexpr_clear(&new_key); *out_id = res->_id; return _Z_RES_OK; } } _z_keyexpr_t ke; _Z_RETURN_IF_ERR(_z_keyexpr_move(&ke, &new_key)); *resources = _z_resource_slist_push_empty(*resources); _z_resource_t *res = _z_resource_slist_value(*resources); res->_refcount = 1; res->_key = ke; res->_id = id == Z_RESOURCE_ID_NONE ? _z_get_resource_id(zn) : id; *out_id = res->_id; return _Z_RES_OK; } z_result_t _z_register_resource(_z_session_t *zn, const _z_wireexpr_t *expr, uint16_t id, _z_transport_peer_common_t *peer, uint16_t *out_id) { _Z_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn)); z_result_t ret = _z_register_resource_inner(zn, expr, id, peer, out_id); _z_session_mutex_unlock(zn); return ret; } z_result_t _z_unregister_resource(_z_session_t *zn, uint16_t id, _z_transport_peer_common_t *peer) { bool is_local = true; uintptr_t mapping = _Z_KEYEXPR_MAPPING_LOCAL; if (peer != NULL) { is_local = false; mapping = (uintptr_t)peer; } _Z_DEBUG("unregistering: id %d, mapping: %d", id, (unsigned int)mapping); _z_session_mutex_lock(zn); _z_resource_slist_t **resources = is_local ? &zn->_local_resources : &peer->_remote_resources; _z_resource_t res = {0}; res._id = id; _z_resource_slist_t *res_ptr = _z_resource_slist_find(*resources, _z_resource_eq, &res); z_result_t ret = _Z_RESOURCE_POSITIVE_REF_COUNT; if (res_ptr == NULL) { ret = _Z_ERR_KEYEXPR_UNKNOWN; } else { _z_resource_slist_value(res_ptr)->_refcount--; if (_z_resource_slist_value(res_ptr)->_refcount == 0) { ret = _Z_RES_OK; *resources = _z_resource_slist_drop_first_filter(*resources, _z_resource_eq, &res); } } _z_session_mutex_unlock(zn); return ret; } void _z_flush_local_resources(_z_session_t *zn) { _z_session_mutex_lock(zn); _z_resource_slist_free(&zn->_local_resources); _z_session_mutex_unlock(zn); } ================================================ FILE: src/session/rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/push.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/reply.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/utils/logging.h" /*------------------ Handle message ------------------*/ static z_result_t _z_handle_declare_inner(_z_session_t *zn, _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { switch (decl->_decl._tag) { case _Z_DECL_KEXPR: { uint16_t _res_id; return _z_register_resource(zn, &decl->_decl._body._decl_kexpr._keyexpr, decl->_decl._body._decl_kexpr._id, peer, &_res_id); } case _Z_UNDECL_KEXPR: _z_unregister_resource(zn, decl->_decl._body._undecl_kexpr._id, peer); break; case _Z_DECL_SUBSCRIBER: return _z_interest_process_declares(zn, decl, peer); case _Z_DECL_QUERYABLE: return _z_interest_process_declares(zn, decl, peer); case _Z_DECL_TOKEN: _Z_RETURN_IF_ERR(_z_liveliness_process_token_declare(zn, decl, peer)); return _z_interest_process_declares(zn, decl, peer); case _Z_UNDECL_SUBSCRIBER: return _z_interest_process_undeclares(zn, &decl->_decl, peer); case _Z_UNDECL_QUERYABLE: return _z_interest_process_undeclares(zn, &decl->_decl, peer); case _Z_UNDECL_TOKEN: _Z_RETURN_IF_ERR(_z_liveliness_process_token_undeclare(zn, decl)); return _z_interest_process_undeclares(zn, &decl->_decl, peer); case _Z_DECL_FINAL: _Z_RETURN_IF_ERR(_z_liveliness_process_declare_final(zn, decl)); // Check that interest id is valid if (!decl->_interest_id.has_value) { _Z_ERROR_RETURN(_Z_ERR_MESSAGE_ZENOH_DECLARATION_UNKNOWN); } return _z_interest_process_declare_final(zn, decl->_interest_id.value, peer); default: _Z_INFO("Received unknown declare tag: %d\n", decl->_decl._tag); break; } return _Z_RES_OK; } static z_result_t _z_handle_declare(_z_session_t *zn, _z_n_msg_declare_t *decl, _z_transport_peer_common_t *peer) { z_result_t ret = _z_handle_declare_inner(zn, decl, peer); _z_n_msg_declare_clear(decl); return ret; } static z_result_t _z_handle_request(_z_transport_common_t *transport, _z_n_msg_request_t *req, z_reliability_t reliability, _z_transport_peer_common_t *peer) { _ZP_UNUSED(reliability); _ZP_UNUSED(transport); _ZP_UNUSED(peer); _z_session_t *zn = _z_transport_common_get_session(transport); _ZP_UNUSED(zn); switch (req->_tag) { case _Z_REQUEST_QUERY: { #if Z_FEATURE_QUERYABLE == 1 // Memory cleaning must be done in the feature layer return _z_trigger_queryables(transport, &req->_body._query, &req->_key, (uint32_t)req->_rid, req->_ext_qos, peer); #else _Z_DEBUG("_Z_REQUEST_QUERY dropped, queryables not supported"); _z_n_msg_request_clear(req); break; #endif } case _Z_REQUEST_PUT: { #if Z_FEATURE_SUBSCRIPTION == 1 _z_msg_put_t put = req->_body._put; // Memory cleaning must be done in the feature layer _Z_RETURN_IF_ERR(_z_trigger_subscriptions_put(zn, &req->_key, &put._payload, &put._encoding, &put._commons._timestamp, req->_ext_qos, &put._attachment, reliability, &put._commons._source_info, peer)); #endif _z_network_message_t final; _z_n_msg_make_response_final(&final, req->_rid); z_result_t ret = _z_send_n_msg(zn, &final, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); #if Z_FEATURE_SUBSCRIPTION == 0 _z_n_msg_request_clear(req); #endif return ret; } case _Z_REQUEST_DEL: { #if Z_FEATURE_SUBSCRIPTION == 1 _z_msg_del_t del = req->_body._del; // Memory cleaning must be done in the feature layer _Z_RETURN_IF_ERR(_z_trigger_subscriptions_del(zn, &req->_key, &del._commons._timestamp, req->_ext_qos, &del._attachment, reliability, &del._commons._source_info, peer)); #endif _z_network_message_t final; _z_n_msg_make_response_final(&final, req->_rid); z_result_t ret = _z_send_n_msg(zn, &final, Z_RELIABILITY_RELIABLE, Z_CONGESTION_CONTROL_BLOCK, NULL); #if Z_FEATURE_SUBSCRIPTION == 0 _z_n_msg_request_clear(req); #endif return ret; } default: _Z_INFO("Received unknown request tag: %d\n", req->_tag); _z_n_msg_request_clear(req); break; } return _Z_RES_OK; } static z_result_t _z_handle_response(_z_session_t *zn, _z_n_msg_response_t *resp, _z_transport_peer_common_t *peer) { #if Z_FEATURE_QUERY == 1 _z_entity_global_id_t replier_id = {.zid = resp->_ext_responder._zid, .eid = resp->_ext_responder._eid}; switch (resp->_tag) { case _Z_RESPONSE_BODY_REPLY: // Memory cleaning must be done in the feature layer return _z_trigger_reply_partial(zn, resp->_request_id, &resp->_key, &resp->_body._reply, &replier_id, peer); case _Z_RESPONSE_BODY_ERR: // Memory cleaning must be done in the feature layer return _z_trigger_reply_err(zn, resp->_request_id, &resp->_body._err, &replier_id); default: _Z_INFO("Received unknown response tag: %d\n", resp->_tag); _z_n_msg_response_clear(resp); break; } #else _z_n_msg_response_clear(resp); _ZP_UNUSED(zn); _ZP_UNUSED(peer); #endif return _Z_RES_OK; } z_result_t _z_handle_network_message(_z_transport_common_t *transport, _z_zenoh_message_t *msg, _z_transport_peer_common_t *peer) { z_result_t ret = _Z_RES_OK; _z_session_t *zn = _z_transport_common_get_session(transport); switch (msg->_tag) { case _Z_N_DECLARE: _Z_DEBUG("Handling _Z_N_DECLARE: %i", msg->_body._declare._decl._tag); ret = _z_handle_declare(zn, &msg->_body._declare, peer); break; case _Z_N_PUSH: _Z_DEBUG("Handling _Z_N_PUSH"); ret = _z_trigger_push(zn, &msg->_body._push, msg->_reliability, peer); break; case _Z_N_REQUEST: _Z_DEBUG("Handling _Z_N_REQUEST"); ret = _z_handle_request(transport, &msg->_body._request, msg->_reliability, peer); break; case _Z_N_RESPONSE: _Z_DEBUG("Handling _Z_N_RESPONSE"); ret = _z_handle_response(zn, &msg->_body._response, peer); break; case _Z_N_RESPONSE_FINAL: _Z_DEBUG("Handling _Z_N_RESPONSE_FINAL"); ret = _z_trigger_reply_final(zn, &msg->_body._response_final); _z_n_msg_response_final_clear(&msg->_body._response_final); break; case _Z_N_INTEREST: { _Z_DEBUG("Handling _Z_N_INTEREST"); _z_n_msg_interest_t *interest = &msg->_body._interest; if ((interest->_interest.flags & _Z_INTEREST_NOT_FINAL_MASK) != 0) { _z_interest_process_interest(zn, &interest->_interest._keyexpr, interest->_interest._id, interest->_interest.flags, peer); } else { _z_interest_process_interest_final(zn, interest->_interest._id); } _z_n_msg_interest_clear(&msg->_body._interest); } break; case _Z_N_OAM: { _Z_DEBUG("Ignoring _Z_N_OAM"); _z_n_msg_oam_clear(&msg->_body._oam); } break; default: _Z_ERROR("Unknown network message ID"); _z_n_msg_clear(msg); break; } return ret; } ================================================ FILE: src/session/scout.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico/link/manager.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/transport/multicast.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_SCOUTING == 1 #define SCOUT_BUFFER_SIZE 32 static _z_hello_slist_t *__z_scout_loop(const _z_wbuf_t *wbf, _z_string_t *locator, unsigned long period, bool exit_on_first) { // Define an empty array _z_hello_slist_t *ret = NULL; z_result_t err = _Z_RES_OK; _z_endpoint_t ep; err = _z_endpoint_from_string(&ep, locator); #if Z_FEATURE_SCOUTING == 1 _z_string_t cmp_str = _z_string_alias_str(UDP_SCHEMA); if ((err == _Z_RES_OK) && _z_string_equals(&ep._locator._protocol, &cmp_str)) { _z_endpoint_clear(&ep); } else #endif if (err == _Z_RES_OK) { _z_endpoint_clear(&ep); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); err = _Z_ERR_TRANSPORT_NOT_AVAILABLE; } if (err == _Z_RES_OK) { _z_link_t zl; memset(&zl, 0, sizeof(_z_link_t)); err = _z_open_link(&zl, locator, NULL); if (err == _Z_RES_OK) { // Send the scout message err = _z_link_send_wbuf(&zl, wbf, NULL); if (err == _Z_RES_OK) { // The receiving buffer _z_zbuf_t zbf = _z_zbuf_make(Z_BATCH_UNICAST_SIZE); z_clock_t start = z_clock_now(); while (z_clock_elapsed_ms(&start) < period) { // Eventually read hello messages _z_zbuf_reset(&zbf); // Read bytes from the socket size_t len = _z_link_recv_zbuf(&zl, &zbf, NULL); if (len == SIZE_MAX) { continue; } _z_scouting_message_t s_msg; err = _z_scouting_message_decode(&s_msg, &zbf); if (err != _Z_RES_OK) { _Z_ERROR("Scouting loop received malformed message"); continue; } switch (_Z_MID(s_msg._header)) { case _Z_MID_HELLO: { _Z_DEBUG("Received _Z_HELLO message"); ret = _z_hello_slist_push_empty(ret); if (ret == NULL) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); err = _Z_ERR_SYSTEM_OUT_OF_MEMORY; break; } _z_hello_t *hello = _z_hello_slist_value(ret); hello->_version = s_msg._body._hello._version; hello->_whatami = s_msg._body._hello._whatami; memcpy(hello->_zid.id, s_msg._body._hello._zid.id, 16); size_t n_loc = _z_locator_array_len(&s_msg._body._hello._locators); if (n_loc > 0) { hello->_locators = _z_string_svec_make(n_loc); for (size_t i = 0; i < n_loc; i++) { _z_string_t s = _z_locator_to_string(&s_msg._body._hello._locators._val[i]); err = _z_string_svec_append(&hello->_locators, &s, true); if (err != _Z_RES_OK) { _Z_ERROR_LOG(err); break; // bail out of locator loop; case-level break follows below } } } else { // @TODO: construct the locator departing from the sock address _z_string_svec_clear(&hello->_locators); } break; } default: { _Z_ERROR_LOG(_Z_ERR_MESSAGE_UNEXPECTED); err = _Z_ERR_MESSAGE_UNEXPECTED; _Z_ERROR("Scouting loop received unexpected message"); break; } } _z_s_msg_clear(&s_msg); if (!_z_hello_slist_is_empty(ret) && exit_on_first) { break; } } _z_link_clear(&zl); _z_zbuf_clear(&zbf); } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); err = _Z_ERR_TRANSPORT_TX_FAILED; _z_link_clear(&zl); } } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_FAILED); err = _Z_ERR_TRANSPORT_OPEN_FAILED; } } _ZP_UNUSED(err); return ret; } _z_hello_slist_t *_z_scout_inner(const z_what_t what, _z_id_t zid, _z_string_t *locator, const uint32_t timeout, const bool exit_on_first) { _z_hello_slist_t *ret = NULL; // Create the buffer to serialize the scout message on _z_wbuf_t wbf = _z_wbuf_make(SCOUT_BUFFER_SIZE, false); // Create and encode the scout message _z_scouting_message_t scout = _z_s_msg_make_scout(what, zid); z_result_t res = _z_scouting_message_encode(&wbf, &scout); if (res != _Z_RES_OK) { _Z_ERROR("Scout message encoding failed with err %d", res); _z_wbuf_clear(&wbf); return NULL; } // Scout on multicast ret = __z_scout_loop(&wbf, locator, timeout, exit_on_first); _z_wbuf_clear(&wbf); return ret; } #else _z_hello_slist_t *_z_scout_inner(const z_what_t what, _z_id_t zid, _z_string_t *locator, const uint32_t timeout, const bool exit_on_first) { _ZP_UNUSED(what); _ZP_UNUSED(zid); _ZP_UNUSED(locator); _ZP_UNUSED(timeout); _ZP_UNUSED(exit_on_first); return NULL; } #endif // Z_FEATURE_SCOUTING == 1 ================================================ FILE: src/session/subscription.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/subscription.h" #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #include "zenoh-pico/net/filtering.h" #include "zenoh-pico/net/sample.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/utils/locality.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_SUBSCRIPTION == 1 #define _Z_SUBINFOS_VEC_SIZE 4 // Arbitrary initial size void _z_unsafe_subscription_cache_invalidate(_z_session_t *zn) { #if Z_FEATURE_RX_CACHE == 1 _z_subscription_lru_cache_clear(&zn->_subscription_cache); #else _ZP_UNUSED(zn); #endif } #if Z_FEATURE_RX_CACHE == 1 int _z_subscription_cache_data_compare(const void *first, const void *second) { const _z_subscription_cache_data_t *first_data = (const _z_subscription_cache_data_t *)first; const _z_subscription_cache_data_t *second_data = (const _z_subscription_cache_data_t *)second; if (first_data->is_remote != second_data->is_remote) { return (int)first_data->is_remote - (int)second_data->is_remote; } return _z_keyexpr_compare(&first_data->ke, &second_data->ke); } #endif // Z_FEATURE_RX_CACHE == 1 void _z_subscription_cache_data_clear(_z_subscription_cache_data_t *val) { _z_subscription_rc_svec_rc_drop(&val->infos); _z_keyexpr_clear(&val->ke); } bool _z_subscription_eq(const _z_subscription_t *other, const _z_subscription_t *this_) { return this_->_id == other->_id; } void _z_subscription_clear(_z_subscription_t *sub) { if (sub->_dropper != NULL) { sub->_dropper(sub->_arg); sub->_dropper = NULL; } _z_declared_keyexpr_clear(&sub->_key); _z_sync_group_notifier_drop(&sub->_session_callback_drop_notifier); _z_sync_group_notifier_drop(&sub->_subscriber_callback_drop_notifier); } _z_subscription_rc_t *__z_get_subscription_by_id(_z_subscription_rc_slist_t *subs, const _z_zint_t id) { _z_subscription_rc_t *ret = NULL; _z_subscription_rc_slist_t *xs = subs; while (xs != NULL) { _z_subscription_rc_t *sub = _z_subscription_rc_slist_value(xs); if (id == _Z_RC_IN_VAL(sub)->_id) { ret = sub; break; } xs = _z_subscription_rc_slist_next(xs); } return ret; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ _z_subscription_rc_t *__unsafe_z_get_subscription_by_id(_z_session_t *zn, _z_subscriber_kind_t kind, const _z_zint_t id) { _z_subscription_rc_slist_t *subs = (kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) ? zn->_subscriptions : zn->_liveliness_subscriptions; return __z_get_subscription_by_id(subs, id); } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static z_result_t __unsafe_z_get_subscriptions_by_key(_z_session_t *zn, _z_subscriber_kind_t kind, const _z_keyexpr_t *key, bool is_remote, _z_subscription_rc_svec_t *sub_infos) { _z_subscription_rc_slist_t *subs = (kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) ? zn->_subscriptions : zn->_liveliness_subscriptions; *sub_infos = _z_subscription_rc_svec_make(_Z_SUBINFOS_VEC_SIZE); _Z_RETURN_ERR_OOM_IF_TRUE(sub_infos->_val == NULL); _z_subscription_rc_slist_t *xs = subs; while (xs != NULL) { // Parse subscription list _z_subscription_rc_t *sub = _z_subscription_rc_slist_value(xs); const _z_subscription_t *sub_val = _Z_RC_IN_VAL(sub); bool origin_allowed = is_remote ? _z_locality_allows_remote(sub_val->_allowed_origin) : _z_locality_allows_local(sub_val->_allowed_origin); if (origin_allowed && _z_keyexpr_intersects(&sub_val->_key._inner, key)) { _z_subscription_rc_t sub_clone = _z_subscription_rc_clone(sub); _Z_CLEAN_RETURN_IF_ERR(_z_subscription_rc_svec_append(sub_infos, &sub_clone, false), _z_subscription_rc_svec_clear(sub_infos)); } xs = _z_subscription_rc_slist_next(xs); } return _Z_RES_OK; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - zn->_mutex_inner */ static z_result_t __unsafe_z_get_subscriptions_rc_by_key(_z_session_t *zn, _z_subscriber_kind_t kind, const _z_keyexpr_t *key, bool is_remote, _z_subscription_rc_svec_rc_t *sub_infos) { *sub_infos = _z_subscription_rc_svec_rc_new_undefined(); z_result_t ret = !_Z_RC_IS_NULL(sub_infos) ? _Z_RES_OK : _Z_ERR_SYSTEM_OUT_OF_MEMORY; _Z_SET_IF_OK(ret, __unsafe_z_get_subscriptions_by_key(zn, kind, key, is_remote, _Z_RC_IN_VAL(sub_infos))); if (ret != _Z_RES_OK) { _z_subscription_rc_svec_rc_drop(sub_infos); } return _Z_RES_OK; } _z_subscription_rc_t _z_get_subscription_by_id(_z_session_t *zn, _z_subscriber_kind_t kind, const _z_zint_t id) { _z_subscription_rc_t out = _z_subscription_rc_null(); _z_session_mutex_lock(zn); _z_subscription_rc_t *sub = __unsafe_z_get_subscription_by_id(zn, kind, id); if (sub != NULL) { out = _z_subscription_rc_clone(sub); } _z_session_mutex_unlock(zn); return out; } _z_subscription_rc_t _z_register_subscription(_z_session_t *zn, _z_subscriber_kind_t kind, _z_subscription_t *s) { _Z_DEBUG(">>> Allocating sub decl for (%.*s)", (int)_z_string_len(&s->_key._inner._keyexpr), _z_string_data(&s->_key._inner._keyexpr)); _z_subscription_rc_t *ret = NULL; _z_subscription_rc_t out = _z_subscription_rc_new_from_val(s); if (_Z_RC_IS_NULL(&out)) { return out; } if (_z_session_mutex_lock_if_open(zn) != _Z_RES_OK) { _z_subscription_rc_drop(&out); *s = _z_subscription_null(); return _z_subscription_rc_null(); } if (kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) { zn->_subscriptions = _z_subscription_rc_slist_push_empty(zn->_subscriptions); ret = _z_subscription_rc_slist_value(zn->_subscriptions); } else { zn->_liveliness_subscriptions = _z_subscription_rc_slist_push_empty(zn->_liveliness_subscriptions); ret = _z_subscription_rc_slist_value(zn->_liveliness_subscriptions); } if (ret == NULL) { _z_subscription_rc_drop(&out); } else { // immediately increase reference count to prevent eventual drop by concurrent session close *ret = _z_subscription_rc_clone(&out); } _z_session_mutex_unlock(zn); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 if (!_Z_RC_IS_NULL(&out) && kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) { _z_subscription_t *sub_val = _Z_RC_IN_VAL(&out); if (_z_locality_allows_local(sub_val->_allowed_origin)) { _z_write_filter_notify_subscriber(zn, &sub_val->_key._inner, sub_val->_allowed_origin, true); } } #endif return out; } z_result_t _z_trigger_liveliness_subscriptions_declare(_z_session_t *zn, const _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer) { _z_encoding_t encoding = _z_encoding_null(); _z_bytes_t payload = _z_bytes_null(); _z_bytes_t attachment = _z_bytes_null(); _z_source_info_t source_info = _z_source_info_null(); _z_wireexpr_t wireexpr2 = _z_wireexpr_alias(wireexpr); return _z_trigger_subscriptions_impl(zn, _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &wireexpr2, &payload, &encoding, Z_SAMPLE_KIND_PUT, timestamp, _Z_N_QOS_DEFAULT, &attachment, Z_RELIABILITY_RELIABLE, &source_info, peer); } z_result_t _z_trigger_liveliness_subscriptions_undeclare(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_timestamp_t *timestamp) { _z_encoding_t encoding = _z_encoding_null(); _z_bytes_t payload = _z_bytes_null(); _z_bytes_t attachment = _z_bytes_null(); _z_wireexpr_t wireexpr = _z_keyexpr_alias_to_wire(keyexpr); _z_source_info_t source_info = _z_source_info_null(); return _z_trigger_subscriptions_impl(zn, _Z_SUBSCRIBER_KIND_LIVELINESS_SUBSCRIBER, &wireexpr, &payload, &encoding, Z_SAMPLE_KIND_DELETE, timestamp, _Z_N_QOS_DEFAULT, &attachment, Z_RELIABILITY_RELIABLE, &source_info, NULL); } static z_result_t _z_subscription_get_infos(_z_session_t *zn, _z_subscriber_kind_t kind, _z_subscription_cache_data_t *out, const _z_wireexpr_t *wireexpr, _z_transport_peer_common_t *peer) { out->is_remote = (peer != NULL); _Z_RETURN_IF_ERR(_z_get_keyexpr_from_wireexpr(zn, &out->ke, wireexpr, peer, true)); _Z_CLEAN_RETURN_IF_ERR(_z_session_mutex_lock_if_open(zn), _z_keyexpr_clear(&out->ke)); _z_subscription_cache_data_t *cache_entry = NULL; z_result_t ret = _Z_RES_OK; #if Z_FEATURE_RX_CACHE == 1 cache_entry = _z_subscription_lru_cache_get(&zn->_subscription_cache, out); if (cache_entry != NULL && cache_entry->is_remote != out->is_remote) { cache_entry = NULL; } #endif if (cache_entry != NULL) { // Copy cache entry out->infos = _z_subscription_rc_svec_rc_clone(&cache_entry->infos); } else { // Construct data and add to cache _Z_SET_IF_OK(ret, __unsafe_z_get_subscriptions_rc_by_key(zn, kind, &out->ke, out->is_remote, &out->infos)); #if Z_FEATURE_RX_CACHE == 1 _z_subscription_cache_data_t cache_storage = _z_subscription_cache_data_null(); cache_storage.infos = _z_subscription_rc_svec_rc_clone(&out->infos); cache_storage.is_remote = out->is_remote; _Z_SET_IF_OK(ret, _z_keyexpr_copy(&cache_storage.ke, &out->ke)); _Z_SET_IF_OK(ret, _z_subscription_lru_cache_insert(&zn->_subscription_cache, &cache_storage)); if (ret != _Z_RES_OK) { _z_subscription_cache_data_clear(&cache_storage); } #endif } _z_session_mutex_unlock(zn); if (ret != _Z_RES_OK) { _z_subscription_cache_data_clear(out); } return ret; } z_result_t _z_trigger_subscriptions_impl(_z_session_t *zn, _z_subscriber_kind_t sub_kind, _z_wireexpr_t *wireexpr, _z_bytes_t *payload, _z_encoding_t *encoding, const _z_zint_t sample_kind, const _z_timestamp_t *timestamp, const _z_n_qos_t qos, _z_bytes_t *attachment, z_reliability_t reliability, _z_source_info_t *source_info, _z_transport_peer_common_t *peer) { _z_subscription_cache_data_t sub_infos = _z_subscription_cache_data_null(); _Z_CLEAN_RETURN_IF_ERR(_z_subscription_get_infos(zn, sub_kind, &sub_infos, wireexpr, peer), _z_wireexpr_clear(wireexpr); _z_encoding_clear(encoding); _z_bytes_drop(payload); _z_bytes_drop(attachment);); const _z_subscription_rc_svec_t *subs = _Z_RC_IN_VAL(&sub_infos.infos); size_t sub_nb = _z_subscription_rc_svec_len(subs); _Z_DEBUG("Triggering %ju subs for key %.*s", (uintmax_t)sub_nb, (int)_z_string_len(&sub_infos.ke._keyexpr), _z_string_data(&sub_infos.ke._keyexpr)); // Create sample z_result_t ret = _Z_RES_OK; _z_sample_t sample; _z_sample_steal_data(&sample, &sub_infos.ke, payload, timestamp, encoding, sample_kind, qos, attachment, reliability, source_info); // Parse subscription infos svec if (sub_nb == 1) { _z_subscription_t *sub_info = _Z_RC_IN_VAL(_z_subscription_rc_svec_get(subs, 0)); sub_info->_callback(&sample, sub_info->_arg); } else { for (size_t i = 0; i < sub_nb; i++) { _z_subscription_t *sub_info = _Z_RC_IN_VAL(_z_subscription_rc_svec_get(subs, i)); if (i + 1 == sub_nb) { sub_info->_callback(&sample, sub_info->_arg); } else { _z_sample_t sample_copy; ret = _z_sample_copy(&sample_copy, &sample); if (ret != _Z_RES_OK) { break; } sub_info->_callback(&sample_copy, sub_info->_arg); _z_sample_clear(&sample_copy); } } } _z_wireexpr_clear(wireexpr); _z_sample_clear(&sample); _z_subscription_cache_data_clear(&sub_infos); return ret; } void _z_unregister_subscription(_z_session_t *zn, _z_subscriber_kind_t kind, _z_subscription_rc_t *sub) { #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 if (kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) { _z_subscription_t *sub_val = _Z_RC_IN_VAL(sub); _z_write_filter_notify_subscriber(zn, &sub_val->_key._inner, sub_val->_allowed_origin, false); } #endif _z_session_mutex_lock(zn); _z_unsafe_subscription_cache_invalidate(zn); if (kind == _Z_SUBSCRIBER_KIND_SUBSCRIBER) { zn->_subscriptions = _z_subscription_rc_slist_drop_first_filter(zn->_subscriptions, _z_subscription_rc_eq, sub); } else { zn->_liveliness_subscriptions = _z_subscription_rc_slist_drop_first_filter(zn->_liveliness_subscriptions, _z_subscription_rc_eq, sub); } _z_session_mutex_unlock(zn); _z_subscription_rc_drop(sub); } void _z_flush_subscriptions(_z_session_t *zn) { _z_subscription_rc_slist_t *subscriptions, *liveliness_subscriptions; _z_session_mutex_lock(zn); _z_unsafe_subscription_cache_invalidate(zn); subscriptions = zn->_subscriptions; liveliness_subscriptions = zn->_liveliness_subscriptions; zn->_subscriptions = _z_subscription_rc_slist_new(); zn->_liveliness_subscriptions = _z_subscription_rc_slist_new(); _z_session_mutex_unlock(zn); _z_subscription_rc_slist_free(&subscriptions); _z_subscription_rc_slist_free(&liveliness_subscriptions); } #else // Z_FEATURE_SUBSCRIPTION == 0 z_result_t _z_trigger_liveliness_subscriptions_declare(_z_session_t *zn, const _z_wireexpr_t *wireexpr, const _z_timestamp_t *timestamp, _z_transport_peer_common_t *peer) { _ZP_UNUSED(zn); _ZP_UNUSED(wireexpr); _ZP_UNUSED(timestamp); _ZP_UNUSED(peer); return _Z_RES_OK; } z_result_t _z_trigger_liveliness_subscriptions_undeclare(_z_session_t *zn, const _z_keyexpr_t *keyexpr, const _z_timestamp_t *timestamp) { _ZP_UNUSED(zn); _ZP_UNUSED(keyexpr); _ZP_UNUSED(timestamp); return _Z_RES_OK; } void _z_unsafe_subscription_cache_invalidate(_z_session_t *zn) { _ZP_UNUSED(zn); } #endif // Z_FEATURE_SUBSCRIPTION == 1 ================================================ FILE: src/session/utils.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/session/utils.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/matching.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/result.h" /*------------------ clone helpers ------------------*/ void _z_timestamp_copy(_z_timestamp_t *dst, const _z_timestamp_t *src) { *dst = *src; } _z_timestamp_t _z_timestamp_duplicate(const _z_timestamp_t *tstamp) { return *tstamp; } void _z_timestamp_move(_z_timestamp_t *dst, _z_timestamp_t *src) { *dst = *src; _z_timestamp_clear(src); } void _z_timestamp_clear(_z_timestamp_t *tstamp) { tstamp->valid = false; tstamp->time = 0; } z_result_t _z_session_generate_zid(_z_id_t *bs, uint8_t size) { z_result_t ret = _Z_RES_OK; z_random_fill((uint8_t *)bs->id, size); return ret; } /*------------------ Init/Free/Close session ------------------*/ z_result_t _z_session_init(_z_session_t *zn, const _z_id_t *zid) { z_result_t ret = _Z_RES_OK; _z_atomic_bool_init(&zn->_is_closed, true); _z_runtime_null(&zn->_runtime); #if Z_FEATURE_MULTI_THREAD == 1 _Z_RETURN_IF_ERR(_z_mutex_init(&zn->_mutex_inner)); ret = _z_mutex_rec_init(&zn->_mutex_transport); if (ret != _Z_RES_OK) { _z_mutex_drop(&zn->_mutex_inner); _Z_ERROR_RETURN(ret); } #if Z_FEATURE_ADMIN_SPACE == 1 ret = _z_mutex_init(&zn->_mutex_admin_space); if (ret != _Z_RES_OK) { _z_mutex_rec_drop(&zn->_mutex_transport); _z_mutex_drop(&zn->_mutex_inner); _Z_ERROR_RETURN(ret); } #endif #endif zn->_mode = Z_WHATAMI_CLIENT; zn->_tp._type = _Z_TRANSPORT_NONE; // Initialize the counters to 1 zn->_entity_id = 1; zn->_resource_id = 1; zn->_query_id = 1; _z_config_init(&zn->_config); #if Z_FEATURE_AUTO_RECONNECT == 1 zn->_declaration_cache = NULL; #endif // Initialize the data structs zn->_local_resources = NULL; #if Z_FEATURE_SUBSCRIPTION == 1 zn->_subscriptions = NULL; zn->_liveliness_subscriptions = NULL; #if Z_FEATURE_RX_CACHE == 1 zn->_subscription_cache = _z_subscription_lru_cache_init(Z_RX_CACHE_SIZE); #endif #endif #if Z_FEATURE_QUERYABLE == 1 zn->_local_queryable = NULL; #if Z_FEATURE_RX_CACHE == 1 zn->_queryable_cache = _z_queryable_lru_cache_init(Z_RX_CACHE_SIZE); #endif #endif #if Z_FEATURE_QUERY == 1 zn->_pending_queries = NULL; #endif #if Z_FEATURE_LIVELINESS == 1 _z_liveliness_init(zn); #endif #if Z_FEATURE_INTEREST == 1 zn->_write_filters = NULL; #endif #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_ADMIN_SPACE == 1 zn->_admin_space_queryable_id = 0; #if Z_FEATURE_CONNECTIVITY == 1 #if Z_FEATURE_QUERYABLE == 1 zn->_admin_space_session_queryable_id = 0; #endif #if Z_FEATURE_PUBLICATION == 1 zn->_admin_space_transport_listener_id = 0; zn->_admin_space_link_listener_id = 0; #endif #endif #endif #if Z_FEATURE_CONNECTIVITY == 1 zn->_connectivity_next_listener_id = 1; _z_connectivity_transport_listener_intmap_init(&zn->_connectivity_transport_event_listeners); _z_connectivity_link_listener_intmap_init(&zn->_connectivity_link_event_listeners); #endif #endif zn->_callback_drop_sync_group = _z_sync_group_null(); _Z_SET_IF_OK(ret, _z_sync_group_create(&zn->_callback_drop_sync_group)); _Z_SET_IF_OK(ret, _z_runtime_init(&zn->_runtime)); #if Z_FEATURE_QUERY if (ret == _Z_RES_OK) { _z_fut_t fut = _z_fut_null(); fut._fut_fn = _z_pending_query_process_timeout_task_fn; fut._fut_arg = zn; ret = _z_fut_handle_is_null(_z_runtime_spawn(&zn->_runtime, &fut)) ? _Z_ERR_FAILED_TO_SPAWN_TASK : _Z_RES_OK; } #endif if (ret != _Z_RES_OK) { #if Z_FEATURE_MULTI_THREAD == 1 #if Z_FEATURE_ADMIN_SPACE == 1 _z_mutex_drop(&zn->_mutex_admin_space); #endif _z_mutex_rec_drop(&zn->_mutex_transport); _z_mutex_drop(&zn->_mutex_inner); #endif _z_sync_group_drop(&zn->_callback_drop_sync_group); _z_runtime_clear(&zn->_runtime); _Z_ERROR_RETURN(ret); } _z_interest_init(zn); zn->_local_zid = *zid; _z_atomic_bool_store(&zn->_is_closed, false, _z_memory_order_release); return ret; } z_result_t _z_session_close(_z_session_t *zn) { // Need to run close sequence unconditionally, since // this is the only way to guarantee that after _z_session_close returns, // no more callbacks will be running _Z_RETURN_IF_ERR(_z_session_mutex_lock(zn)); _z_atomic_bool_store(&zn->_is_closed, true, _z_memory_order_release); _z_session_mutex_unlock(zn); // stop the runtime to prevent spawning new tasks // this will also ensure that no callbacks can be called in response to message reception inside read task, // session sync group at the end of the call might still be needed in case there are any historical // callbacks currently executing, like in the case of liveliness subscribers/ matching listeners / connectivity // events _Z_RETURN_IF_ERR(_z_runtime_stop(&zn->_runtime)); _Z_RETURN_IF_ERR(_z_session_mutex_lock(zn)); #if Z_FEATURE_AUTO_RECONNECT == 1 _z_network_message_slist_free(&zn->_declaration_cache); #endif _z_session_mutex_unlock(zn); _z_flush_local_resources(zn); #if Z_FEATURE_SUBSCRIPTION == 1 _z_flush_subscriptions(zn); #endif #if Z_FEATURE_QUERYABLE == 1 // Admin space querable cleanup will occur as part of queryable cleanup _z_flush_session_queryable(zn); #endif #if Z_FEATURE_QUERY == 1 _z_flush_pending_queries(zn); #endif #if Z_FEATURE_LIVELINESS == 1 _z_liveliness_clear(zn); #endif _z_flush_interest(zn); #ifdef Z_FEATURE_UNSTABLE_API #if Z_FEATURE_CONNECTIVITY == 1 _Z_RETURN_IF_ERR(_z_session_mutex_lock(zn)); _z_connectivity_transport_listener_intmap_clear(&zn->_connectivity_transport_event_listeners); _z_connectivity_link_listener_intmap_clear(&zn->_connectivity_link_event_listeners); zn->_connectivity_next_listener_id = 1; _z_session_mutex_unlock(zn); #endif #if Z_FEATURE_ADMIN_SPACE == 1 _z_session_admin_space_mutex_lock(zn); zn->_admin_space_queryable_id = 0; #if Z_FEATURE_CONNECTIVITY == 1 #if Z_FEATURE_QUERYABLE == 1 zn->_admin_space_session_queryable_id = 0; #endif #if Z_FEATURE_PUBLICATION == 1 zn->_admin_space_transport_listener_id = 0; zn->_admin_space_link_listener_id = 0; #endif #endif _z_session_admin_space_mutex_unlock(zn); #endif #endif // Despite stopping runtime at the very beginning, we still need to wait for possible historical callbacks // from liveliness subscribers, matching listeners, connectivity events listeners, etc // to finish _z_sync_group_wait(&zn->_callback_drop_sync_group); return _Z_RES_OK; } void _z_session_clear(_z_session_t *zn) { _z_session_close(zn); _z_runtime_clear(&zn->_runtime); _z_config_clear(&zn->_config); _z_session_transport_mutex_lock(zn); _z_transport_clear(&zn->_tp); _z_session_transport_mutex_unlock(zn); #if Z_FEATURE_MULTI_THREAD == 1 #if Z_FEATURE_ADMIN_SPACE == 1 _z_mutex_drop(&zn->_mutex_admin_space); #endif _z_mutex_rec_drop(&zn->_mutex_transport); _z_mutex_drop(&zn->_mutex_inner); #endif // Z_FEATURE_MULTI_THREAD == 1 _z_sync_group_drop(&zn->_callback_drop_sync_group); } ================================================ FILE: src/system/arduino/esp32/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_ARDUINO_ESP32) #include #include #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/system_error.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return z_random_u32(); } uint16_t z_random_u16(void) { return z_random_u32(); } uint32_t z_random_u32(void) { return esp_random(); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { esp_fill_random(buf, len); } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return heap_caps_malloc(size, MALLOC_CAP_8BIT); } void *z_realloc(void *ptr, size_t size) { return heap_caps_realloc(ptr, size, MALLOC_CAP_8BIT); } void z_free(void *ptr) { heap_caps_free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 // This wrapper is only used for ESP32. // In FreeRTOS, tasks created using xTaskCreate must end with vTaskDelete. // A task function should __not__ simply return. typedef struct { void *(*_fun)(void *); void *_arg; EventGroupHandle_t join_event; } z_task_arg; void z_task_wrapper(z_task_arg *targ) { targ->_fun(targ->_arg); // Notify the deleter task that this task is done xEventGroupSetBits(targ->join_event, 1); // No one else uses this task argument after this z_free(targ); // Put the task in a safe state to be culled by another task vTaskSuspend(NULL); } static z_task_attr_t z_default_task_attr = { .name = "", .priority = configMAX_PRIORITIES / 2, .stack_depth = 5120, #if (configSUPPORT_STATIC_ALLOCATION == 1) .static_allocation = false, .stack_buffer = NULL, .task_buffer = NULL, #endif /* SUPPORT_STATIC_ALLOCATION */ }; /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { if (attr == NULL) { attr = &z_default_task_attr; } z_task_arg *z_arg = (z_task_arg *)z_malloc(sizeof(z_task_arg)); if (z_arg == NULL) { return -1; } z_arg->_fun = fun; z_arg->_arg = arg; task->join_event = xEventGroupCreate(); z_arg->join_event = task->join_event; #if (configSUPPORT_STATIC_ALLOCATION == 1) if (attr->static_allocation) { task->handle = xTaskCreateStatic((void *)z_task_wrapper, attr->name, attr->stack_depth, z_arg, attr->priority, attr->stack_buffer, attr->task_buffer); if (task->handle == NULL) { z_free(z_arg); return -1; } } else { #endif /* SUPPORT_STATIC_ALLOCATION */ if (xTaskCreate((void *)z_task_wrapper, attr->name, attr->stack_depth, z_arg, attr->priority, &task->handle) != pdPASS) { z_free(z_arg); return -1; } #if (configSUPPORT_STATIC_ALLOCATION == 1) } #endif /* SUPPORT_STATIC_ALLOCATION */ return _Z_RES_OK; } z_result_t _z_task_join(_z_task_t *task) { xEventGroupWaitBits(task->join_event, 1, pdFALSE, pdFALSE, portMAX_DELAY); return _z_task_cancel(task); } z_result_t _z_task_detach(_z_task_t *task) { // Note: task/thread detach not supported on FreeRTOS API, so we force its deletion instead. return _z_task_cancel(task); } z_result_t _z_task_cancel(_z_task_t *task) { vTaskDelete(task->handle); task->handle = NULL; return _Z_RES_OK; } void _z_task_exit(void) { vTaskDelete(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; vEventGroupDelete(ptr->join_event); z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return task->handle; } _z_task_id_t _z_task_current_id(void) { return xTaskGetCurrentTaskHandle(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_init(m, NULL)); } z_result_t _z_mutex_drop(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_unlock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { pthread_mutexattr_t attr; _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_init(&attr)); _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); _Z_RETURN_IF_SYS_ERR(pthread_mutex_init(m, &attr)); _Z_CHECK_SYS_ERR(pthread_mutexattr_destroy(&attr)); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); _Z_CHECK_SYS_ERR(pthread_cond_init(cv, &attr)); } z_result_t _z_condvar_drop(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_destroy(cv)); } z_result_t _z_condvar_signal(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_signal(cv)); } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_broadcast(cv)); } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_cond_wait(cv, m)); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { int error = pthread_cond_timedwait(cv, m, abstime); if (error == ETIMEDOUT) { return Z_ETIMEDOUT; } _Z_CHECK_SYS_ERR(error); } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { _Z_CHECK_SYS_ERR(usleep(time)); } z_result_t z_sleep_ms(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_ms(&start) < time) { z_result_t ret = z_sleep_us(1000); if (ret < 0) { return ret; } } return 0; } z_result_t z_sleep_s(size_t time) { _Z_CHECK_SYS_ERR(sleep(time)); } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = now.tv_sec; t->nanos = now.tv_usec * 1000; return 0; } #endif /* defined(ZENOH_ARDUINO_ESP32) */ ================================================ FILE: src/system/arduino/opencr/network.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/config.h" #if defined(ZENOH_ARDUINO_OPENCR) #include #include #include #include #include #include extern "C" { #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { _ZP_UNUSED(iter); _ZP_UNUSED(timeout_ms); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on OpenCR port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_SERIAL == 1 #error "Serial not supported yet on OpenCR port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on OpenCR port of Zenoh-Pico" #endif } // extern "C" #endif /* defined(ZENOH_ARDUINO_OPENCR) */ ================================================ FILE: src/system/arduino/opencr/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return random(0xFF); } uint16_t z_random_u16(void) { return random(0xFFFF); } uint32_t z_random_u32(void) { return random(0xFFFFFFFF); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { for (size_t i = 0; i < len; i++) { ((uint8_t *)buf)[i] = z_random_u8(); } } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { // return pvPortMalloc(size); // FIXME: Further investigation is required to understand // why pvPortMalloc or pvPortMallocAligned are failing return malloc(size); } void *z_realloc(void *ptr, size_t size) { // Not implemented by the platform return NULL; } void z_free(void *ptr) { // vPortFree(ptr); // FIXME: Further investigation is required to understand // why vPortFree or vPortFreeAligned are failing return free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 #error "Multi-threading not supported yet on OpenCR port. Disable it by defining Z_FEATURE_MULTI_THREAD=0" /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { return -1; } z_result_t _z_task_join(_z_task_t *task) { return -1; } z_result_t _z_task_detach(_z_task_t *task) { return -1; } z_result_t _z_task_cancel(_z_task_t *task) { return -1; } void _z_task_exit(void) {} void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return NULL; } _z_task_id_t _z_task_current_id(void) { return NULL; } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return true; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { return -1; } z_result_t _z_mutex_drop(_z_mutex_t *m) { return -1; } z_result_t _z_mutex_lock(_z_mutex_t *m) { return -1; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { return -1; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { return -1; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { return -1; } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { return -1; } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { return -1; } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { return -1; } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { return -1; } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { return -1; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { return -1; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { return -1; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { return -1; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { return -1; } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { return -1; } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { delay_us(time); return 0; } z_result_t z_sleep_ms(size_t time) { delay_ms(time); return 0; } z_result_t z_sleep_s(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_s(&start) < time) { z_sleep_ms(1000); } return 0; } /*------------------ Instant ------------------*/ void __z_clock_gettime(z_clock_t *ts) { uint64_t m = millis(); ts->tv_sec = m / (uint64_t)1000000; ts->tv_nsec = (m % (uint64_t)1000000) * (uint64_t)1000; } z_clock_t z_clock_now(void) { z_clock_t now; __z_clock_gettime(&now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = now.tv_sec; t->nanos = now.tv_usec * 1000; return 0; } ================================================ FILE: src/system/common/platform.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/api/olv_macros.h" #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Thread ------------------*/ _Z_OWNED_FUNCTIONS_SYSTEM_IMPL(_z_task_t, task) z_result_t z_task_init(z_owned_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { return _z_task_init(&task->_val, attr, fun, arg); } z_result_t z_task_join(z_moved_task_t *task) { _z_task_t *ptr = &task->_this._val; z_result_t ret = _z_task_join(ptr); return ret; } z_result_t z_task_detach(z_moved_task_t *task) { _z_task_t *ptr = &task->_this._val; z_result_t ret = _z_task_detach(ptr); return ret; } z_result_t z_task_drop(z_moved_task_t *task) { return z_task_detach(task); } /*------------------ Mutex ------------------*/ _Z_OWNED_FUNCTIONS_SYSTEM_IMPL(_z_mutex_t, mutex) z_result_t z_mutex_init(z_owned_mutex_t *m) { return _z_mutex_init(&m->_val); } z_result_t z_mutex_drop(z_moved_mutex_t *m) { return _z_mutex_drop(&m->_this._val); } z_result_t z_mutex_lock(z_loaned_mutex_t *m) { return _z_mutex_lock(m); } z_result_t z_mutex_try_lock(z_loaned_mutex_t *m) { return _z_mutex_try_lock(m); } z_result_t z_mutex_unlock(z_loaned_mutex_t *m) { return _z_mutex_unlock(m); } /*------------------ CondVar ------------------*/ _Z_OWNED_FUNCTIONS_SYSTEM_IMPL(_z_condvar_t, condvar) z_result_t z_condvar_init(z_owned_condvar_t *cv) { return _z_condvar_init(&cv->_val); } z_result_t z_condvar_drop(z_moved_condvar_t *cv) { return _z_condvar_drop(&cv->_this._val); } z_result_t z_condvar_signal(z_loaned_condvar_t *cv) { return _z_condvar_signal(cv); } z_result_t z_condvar_wait(z_loaned_condvar_t *cv, z_loaned_mutex_t *m) { return _z_condvar_wait(cv, m); } z_result_t z_condvar_wait_until(z_loaned_condvar_t *cv, z_loaned_mutex_t *m, const z_clock_t *abstime) { return _z_condvar_wait_until(cv, m, abstime); } #endif // Z_FEATURE_MULTI_THREAD == 1 ================================================ FILE: src/system/emscripten/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { _ZP_UNUSED(iter); _ZP_UNUSED(timeout_ms); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_LINK_TCP == 1 #error "TCP not supported yet on Emscripten port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 #error "UDP not supported yet on Emscripten port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on Emscripten port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_SERIAL == 1 #error "Serial not supported yet on Emscripten port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on Emscripten port of Zenoh-Pico" #endif ================================================ FILE: src/system/emscripten/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/system_error.h" #include "zenoh-pico/system/platform.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return (emscripten_random() * 255.0); } uint16_t z_random_u16(void) { return (emscripten_random() * 65535.0); } uint32_t z_random_u32(void) { return (emscripten_random() * 4294967295.0); } uint64_t z_random_u64(void) { return (emscripten_random() * 18446744073709551615.0); } void z_random_fill(void *buf, size_t len) { for (size_t i = 0; i < len; i++) { *((uint8_t *)buf) = z_random_u8(); } } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return malloc(size); } void *z_realloc(void *ptr, size_t size) { return realloc(ptr, size); } void z_free(void *ptr) { free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, pthread_attr_t *attr, void *(*fun)(void *), void *arg) { _Z_CHECK_SYS_ERR(pthread_create(task, attr, fun, arg)); } z_result_t _z_task_join(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_join(*task, NULL)); } z_result_t _z_task_detach(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_detach(*task)); } z_result_t _z_task_cancel(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_cancel(*task)); } void _z_task_exit(void) { pthread_exit(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return *task; } _z_task_id_t _z_task_current_id(void) { return pthread_self(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return pthread_equal(*l, *r) != 0; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_init(m, NULL)); } z_result_t _z_mutex_drop(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_unlock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { pthread_mutexattr_t attr; _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_init(&attr)); _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); _Z_RETURN_IF_SYS_ERR(pthread_mutex_init(m, &attr)); _Z_CHECK_SYS_ERR(pthread_mutexattr_destroy(&attr)); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); _Z_CHECK_SYS_ERR(pthread_cond_init(cv, &attr)); } z_result_t _z_condvar_drop(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_destroy(cv)); } z_result_t _z_condvar_signal(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_signal(cv)); } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_broadcast(cv)); } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_cond_wait(cv, m)); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { struct timespec ts; ts.tv_sec = (time_t)(*abstime / 1000); ts.tv_nsec = (long)((*abstime - (ts.tv_sec * 1000)) * 1000000); int error = pthread_cond_timedwait(cv, m, &ts); if (error == ETIMEDOUT) { return Z_ETIMEDOUT; } _Z_CHECK_SYS_ERR(error); } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { emscripten_sleep((time / 1000) + (time % 1000 == 0 ? 0 : 1)); return 0; } z_result_t z_sleep_ms(size_t time) { emscripten_sleep(time); return 0; } z_result_t z_sleep_s(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_s(&start) < time) { z_sleep_ms(1000); } return 0; } unsigned long z_time_elapsed_ms_since(z_time_t *time, z_time_t *epoch) { unsigned long elapsed = *time > *epoch ? *time - *epoch : 0; return elapsed; } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { return z_time_now(); } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { return z_time_elapsed_ms_since(instant, epoch) * 1000; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { return z_time_elapsed_ms_since(instant, epoch); } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { return z_time_elapsed_ms_since(instant, epoch) / 1000; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { return z_clock_elapsed_ms(instant) * 1000; } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { return z_time_elapsed_ms(instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { return z_time_elapsed_ms(instant) / 1000; } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { *clock += (double)(duration / 1000); } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { *clock += (double)duration; } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { *clock += (double)(duration * 1000); } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { return emscripten_get_now(); } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { snprintf(buf, buflen, "%f", z_time_now()); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { return z_time_elapsed_ms(time) * 1000; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now = emscripten_get_now(); return z_time_elapsed_ms_since(&now, time); } unsigned long z_time_elapsed_s(z_time_t *time) { return z_time_elapsed_ms(time) / 1000; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { double date = emscripten_date_now(); t->secs = (uint32_t)(date / 1000); t->nanos = (uint32_t)((date - t->secs * 1000) * 1000000); return 0; } ================================================ FILE: src/system/espidf/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_ESPIDF) #include #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/system_error.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return z_random_u32(); } uint16_t z_random_u16(void) { return z_random_u32(); } uint32_t z_random_u32(void) { return esp_random(); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { esp_fill_random(buf, len); } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return heap_caps_malloc(size, MALLOC_CAP_8BIT); } void *z_realloc(void *ptr, size_t size) { return heap_caps_realloc(ptr, size, MALLOC_CAP_8BIT); } void z_free(void *ptr) { heap_caps_free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 // This wrapper is only used for ESP32. // In FreeRTOS, tasks created using xTaskCreate must end with vTaskDelete. // A task function should __not__ simply return. typedef struct { void *(*fun)(void *); void *arg; EventGroupHandle_t join_event; } z_task_arg; static void z_task_wrapper(void *arg) { z_task_arg *targ = (z_task_arg *)arg; targ->fun(targ->arg); xEventGroupSetBits(targ->join_event, 1); vTaskDelete(NULL); } static z_task_attr_t z_default_task_attr = { .name = "", .priority = configMAX_PRIORITIES / 2, .stack_depth = 5120, #if (configSUPPORT_STATIC_ALLOCATION == 1) .static_allocation = false, .stack_buffer = NULL, .task_buffer = NULL, #endif /* SUPPORT_STATIC_ALLOCATION */ }; /*------------------ Thread ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *arg_attr, void *(*fun)(void *), void *arg) { z_task_attr_t *attr = arg_attr; z_task_arg *z_arg = (z_task_arg *)z_malloc(sizeof(z_task_arg)); if (z_arg == NULL) { return -1; } z_arg->fun = fun; z_arg->arg = arg; task->join_event = xEventGroupCreate(); z_arg->join_event = task->join_event; if (attr == NULL) { attr = &z_default_task_attr; } #if (configSUPPORT_STATIC_ALLOCATION == 1) if (attr->static_allocation) { task->handle = xTaskCreateStatic(z_task_wrapper, attr->name, attr->stack_depth, z_arg, attr->priority, attr->stack_buffer, attr->task_buffer); if (task->handle == NULL) { return -1; } } else { #endif /* SUPPORT_STATIC_ALLOCATION */ if (xTaskCreate(z_task_wrapper, attr->name, attr->stack_depth, z_arg, attr->priority, &task->handle) != pdPASS) { return -1; } #if (configSUPPORT_STATIC_ALLOCATION == 1) } #endif /* SUPPORT_STATIC_ALLOCATION */ return 0; } z_result_t _z_task_join(_z_task_t *task) { xEventGroupWaitBits(task->join_event, 1, pdFALSE, pdFALSE, portMAX_DELAY); return 0; } z_result_t _z_task_detach(_z_task_t *task) { // Not implemented _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_result_t z_task_cancel(_z_task_t *task) { vTaskDelete(task->handle); return 0; } void _z_task_exit(void) { vTaskDelete(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr->join_event); z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return task->handle; } _z_task_id_t _z_task_current_id(void) { return xTaskGetCurrentTaskHandle(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_init(m, NULL)); } z_result_t _z_mutex_drop(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_unlock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { pthread_mutexattr_t attr; _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_init(&attr)); _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); _Z_RETURN_IF_SYS_ERR(pthread_mutex_init(m, &attr)); _Z_CHECK_SYS_ERR(pthread_mutexattr_destroy(&attr)); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); _Z_CHECK_SYS_ERR(pthread_cond_init(cv, &attr)); } z_result_t _z_condvar_drop(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_destroy(cv)); } z_result_t _z_condvar_signal(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_signal(cv)); } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_broadcast(cv)); } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_cond_wait(cv, m)); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { int error = pthread_cond_timedwait(cv, m, abstime); if (error == ETIMEDOUT) { return Z_ETIMEDOUT; } _Z_CHECK_SYS_ERR(error); } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { vTaskDelay(pdMS_TO_TICKS(time / 1000)); return _Z_RES_OK; } z_result_t z_sleep_ms(size_t time) { vTaskDelay(pdMS_TO_TICKS(time)); return _Z_RES_OK; } z_result_t z_sleep_s(size_t time) { vTaskDelay(pdMS_TO_TICKS(time * 1000)); return _Z_RES_OK; } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = now.tv_sec; t->nanos = now.tv_usec * 1000; return 0; } #endif /* defined(ZENOH_ESPIDF) */ ================================================ FILE: src/system/flipper/network.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } #if Z_FEATURE_LINK_TCP == 1 #error "TCP is not supported on Flipper port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_UDP_UNICAST == 1 || Z_FEATURE_LINK_UDP_MULTICAST == 1 #error "UDP is not supported on Flipper port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported on Flipper port of Zenoh-Pico" #endif ================================================ FILE: src/system/flipper/system.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/result.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return random(); } uint16_t z_random_u16(void) { return random(); } uint32_t z_random_u32(void) { return random(); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void* buf, size_t len) { for (size_t i = 0; i < len; i++) { ((uint8_t*)buf)[i] = z_random_u8(); } } /*------------------ Memory ------------------*/ void* z_malloc(size_t size) { if (!size) { return NULL; } return malloc(size); } void* z_realloc(void* ptr, size_t size) { if (!size) { free(ptr); return NULL; } return realloc(ptr, size); } void z_free(void* ptr) { return free(ptr); } /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t* task, z_task_attr_t* attr, void* (*fun)(void*), void* arg) { if (task == NULL) { return -1; } uint32_t stack_size = FLIPPER_DEFAULT_THREAD_STACK_SIZE; if (attr) { stack_size = *attr; } *task = furi_thread_alloc_ex(NULL, stack_size, (FuriThreadCallback)fun, arg); if (*task == NULL) { return -1; } furi_thread_start(*task); return _Z_RES_OK; } z_result_t _z_task_join(_z_task_t* task) { if (task == NULL) { return -1; } return furi_thread_join(*task); } z_result_t _z_task_detach(_z_task_t* task) { return -1; } z_result_t _z_task_cancel(_z_task_t* task) { return -1; } void _z_task_exit(void) {} void _z_task_free(_z_task_t** task) { if (task == NULL || *task == NULL) { return; } furi_thread_free(**task); z_free(*task); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t* task) { return furi_thread_get_id(*task); } _z_task_id_t _z_task_current_id(void) { return furi_thread_get_current_id(); } bool _z_task_id_equal(const _z_task_id_t* l, const _z_task_id_t* r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t* m) { if (m == NULL) { return -1; } *m = furi_mutex_alloc(FuriMutexTypeRecursive); return (*m != 0) ? _Z_RES_OK : _Z_ERR_SYSTEM_TASK_FAILED; } z_result_t _z_mutex_drop(_z_mutex_t* m) { if (m == NULL) { return -1; } if (*m == 0) { return 0; } furi_mutex_free(*m); *m = NULL; return 0; } z_result_t _z_mutex_lock(_z_mutex_t* m) { if (m == NULL) { return -1; } if (*m == 0) { return 0; } return furi_mutex_acquire(*m, FuriWaitForever); } z_result_t _z_mutex_try_lock(_z_mutex_t* m) { return -1; } z_result_t _z_mutex_unlock(_z_mutex_t* m) { if (m == NULL) { return -1; } if (*m == 0) { return 0; } return furi_mutex_release(*m); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t* cv) { return -1; } z_result_t _z_condvar_drop(_z_condvar_t* cv) { return -1; } z_result_t _z_condvar_signal(_z_condvar_t* cv) { return -1; } z_result_t _z_condvar_signal_all(_z_condvar_t* cv) { return -1; } z_result_t _z_condvar_wait(_z_condvar_t* cv, _z_mutex_t* m) { return -1; } z_result_t _z_condvar_wait_until(_z_condvar_t* cv, _z_mutex_t* m, const z_clock_t* abstime) { return -1; } /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { furi_delay_us(time); return 0; } z_result_t z_sleep_ms(size_t time) { furi_delay_ms(time); return 0; } z_result_t z_sleep_s(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_s(&start) < time) { z_sleep_ms(1000); } return 0; } /*------------------ Instant ------------------*/ void __z_clock_gettime(z_clock_t* ts) { uint64_t m = millis(); ts->tv_sec = m / (uint64_t)1000000; ts->tv_nsec = (m % (uint64_t)1000000) * (uint64_t)1000; } z_clock_t z_clock_now(void) { z_clock_t now; __z_clock_gettime(&now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t* instant, z_clock_t* epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t* instant, z_clock_t* epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t* instant, z_clock_t* epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t* instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t* instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t* instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t* clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t* clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t* clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char* z_time_now_as_str(char* const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t* time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t* time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t* time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } char* strncat(char* dest, const char* src, size_t dest_size) { size_t dest_len = strlen(dest); size_t i; for (i = 0; i < dest_size && src[i] != '\0'; i++) { dest[dest_len + i] = src[i]; } dest[dest_len + i] = '\0'; return dest; } char* strpbrk(const char* str, const char* charset) { while (*str != '\0') { const char* c = charset; while (*c != '\0') { if (*str == *c) { return (char*)str; } c++; } str++; } return NULL; } size_t strftime(char* s, size_t max, const char* format, const struct tm* tm) { // not supported s[0] = 0; return 0; } int gettimeofday(struct timeval* __restrict __p, void* __restrict __tz) { // not supported __p->tv_sec = 0; __p->tv_usec = 0; return 0; } struct tm* localtime(const time_t* timep) { // not supported static struct tm t; return &t; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch* t) { return -1; } ================================================ FILE: src/system/freertos/freertos_plus_tcp/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" // FreeRTOS includes #include "FreeRTOS.h" #include "FreeRTOS_IP.h" #include "FreeRTOS_Sockets.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { TickType_t option_value = blocking ? pdMS_TO_TICKS(Z_CONFIG_SOCKET_TIMEOUT) : 0; BaseType_t result = FreeRTOS_setsockopt(sock->_socket, 0, FREERTOS_SO_RCVTIMEO, &option_value, sizeof(option_value)); if (result != pdPASS) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { FreeRTOS_closesocket(sock->_socket); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { z_result_t ret = _Z_RES_OK; _z_socket_wait_iter_reset(iter); if (!_z_socket_wait_iter_next(iter)) { return _Z_RES_OK; } SocketSet_t socketSet = FreeRTOS_CreateSocketSet(); if (socketSet == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } do { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, false); FreeRTOS_FD_SET(sock->_socket, socketSet, eSELECT_READ); } while (_z_socket_wait_iter_next(iter)); BaseType_t result = FreeRTOS_select(socketSet, pdMS_TO_TICKS(timeout_ms)); if (result != 0) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FreeRTOS_FD_ISSET(sock->_socket, socketSet) != 0); } FreeRTOS_DeleteSocketSet(socketSet); return ret; } #if Z_FEATURE_LINK_UDP_MULTICAST == 1 #error "UDP Multicast not supported yet on FreeRTOS-Plus-TCP port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on FreeRTOS-Plus-TCP port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_SERIAL == 1 #error "Serial not supported yet on FreeRTOS-Plus-TCP port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on FreeRTOS-Plus-TCP port of Zenoh-Pico" #endif ================================================ FILE: src/system/freertos/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // Copyright (c) 2023 Fictionlab sp. z o.o. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // Błażej Sowa, #include #include #include #include #include #if defined(ZENOH_FREERTOS_PLUS_TCP) #include "FreeRTOS_IP.h" #elif defined(ZENOH_FREERTOS_LWIP) #include "lwip/arch.h" #else #error "FreeRTOS System Implementation was used but no compatible network stack was selected" #endif #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return z_random_u32(); } uint16_t z_random_u16(void) { return z_random_u32(); } uint32_t z_random_u32(void) { #if defined(ZENOH_FREERTOS_PLUS_TCP) uint32_t ret = 0; xApplicationGetRandomNumber(&ret); return ret; #elif defined(ZENOH_FREERTOS_LWIP) return LWIP_RAND(); #else #error "FreeRTOS System Implementation was used but no compatible network stack was selected" #endif } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { uint8_t *p = (uint8_t *)buf; for (size_t i = 0; i < len; i++) { p[i] = z_random_u8(); } } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { if (size == 0) { return NULL; } return pvPortMalloc(size); } void *z_realloc(void *ptr, size_t size) { _ZP_UNUSED(ptr); _ZP_UNUSED(size); // realloc not implemented in FreeRTOS return NULL; } void z_free(void *ptr) { vPortFree(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Thread ------------------*/ static void z_task_wrapper(void *arg) { _z_task_t *task = (_z_task_t *)arg; // Run the task function task->fun(task->arg); // Notify the joiner that the task has finished xEventGroupSetBits(task->join_event, 1); // In FreeRTOS, when a task deletes itself, it adds itself to a list of tasks awaiting to be terminated by the idle // task. There is no guarantee when exactly that will happen, which could lead to race conditions on freeing the // task resources. To avoid this, we suspend the task indefinitely and delete this task from another task running // z_task_join or z_task_detach. vTaskSuspend(NULL); } static z_task_attr_t z_default_task_attr = { .name = "", .priority = configMAX_PRIORITIES / 2, .stack_depth = 5120, #if (configSUPPORT_STATIC_ALLOCATION == 1) .static_allocation = false, .stack_buffer = NULL, .task_buffer = NULL, #endif /* SUPPORT_STATIC_ALLOCATION */ }; /*------------------ Thread ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { task->fun = fun; task->arg = arg; #if (configSUPPORT_STATIC_ALLOCATION == 1) task->join_event = xEventGroupCreateStatic(&task->join_event_buffer); #else task->join_event = xEventGroupCreate(); #endif /* SUPPORT_STATIC_ALLOCATION */ if (attr == NULL) { attr = &z_default_task_attr; } #if (configSUPPORT_STATIC_ALLOCATION == 1) if (attr->static_allocation) { task->handle = xTaskCreateStatic(z_task_wrapper, attr->name, attr->stack_depth, task, attr->priority, attr->stack_buffer, attr->task_buffer); if (task->handle == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } } else { #endif /* SUPPORT_STATIC_ALLOCATION */ if (xTaskCreate(z_task_wrapper, attr->name, attr->stack_depth, task, attr->priority, &task->handle) != pdPASS) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if (configSUPPORT_STATIC_ALLOCATION == 1) } #endif /* SUPPORT_STATIC_ALLOCATION */ return _Z_RES_OK; } z_result_t _z_task_join(_z_task_t *task) { xEventGroupWaitBits(task->join_event, 1, pdFALSE, pdFALSE, portMAX_DELAY); taskENTER_CRITICAL(); if (task->handle != NULL) { vTaskDelete(task->handle); task->handle = NULL; } taskEXIT_CRITICAL(); return _Z_RES_OK; } z_result_t _z_task_detach(_z_task_t *task) { _ZP_UNUSED(task); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_result_t _z_task_cancel(_z_task_t *task) { taskENTER_CRITICAL(); if (task->handle != NULL) { vTaskDelete(task->handle); task->handle = NULL; } taskEXIT_CRITICAL(); xEventGroupSetBits(task->join_event, 1); return _Z_RES_OK; } void _z_task_exit(void) { vTaskDelete(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; vEventGroupDelete(ptr->join_event); z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return task->handle; } _z_task_id_t _z_task_current_id(void) { return xTaskGetCurrentTaskHandle(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { #if (configSUPPORT_STATIC_ALLOCATION == 1) m->handle = xSemaphoreCreateRecursiveMutexStatic(&m->buffer); #else m->handle = xSemaphoreCreateRecursiveMutex(); #endif /* SUPPORT_STATIC_ALLOCATION */ return m->handle == NULL ? _Z_ERR_GENERIC : _Z_RES_OK; } z_result_t _z_mutex_drop(_z_mutex_t *m) { vSemaphoreDelete(m->handle); return _Z_RES_OK; } z_result_t _z_mutex_lock(_z_mutex_t *m) { return xSemaphoreTakeRecursive(m->handle, portMAX_DELAY) == pdTRUE ? _Z_RES_OK : _Z_ERR_GENERIC; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { return xSemaphoreTakeRecursive(m->handle, 0) == pdTRUE ? _Z_RES_OK : _Z_ERR_GENERIC; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { return xSemaphoreGiveRecursive(m->handle) == pdTRUE ? _Z_RES_OK : _Z_ERR_GENERIC; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { return _z_mutex_init(m); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { return _z_mutex_drop(m); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { return _z_mutex_lock(m); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { return _z_mutex_try_lock(m); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { return _z_mutex_unlock(m); } /*------------------ CondVar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if (configSUPPORT_STATIC_ALLOCATION == 1) cv->mutex = xSemaphoreCreateMutexStatic(&cv->mutex_buffer); cv->sem = xSemaphoreCreateCountingStatic((UBaseType_t)(~0), 0, &cv->sem_buffer); #else cv->mutex = xSemaphoreCreateMutex(); cv->sem = xSemaphoreCreateCounting((UBaseType_t)(~0), 0); #endif /* SUPPORT_STATIC_ALLOCATION */ cv->waiters = 0; if (!cv->mutex || !cv->sem) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } vSemaphoreDelete(cv->sem); vSemaphoreDelete(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); if (cv->waiters > 0) { xSemaphoreGive(cv->sem); cv->waiters--; } xSemaphoreGive(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); while (cv->waiters > 0) { xSemaphoreGive(cv->sem); cv->waiters--; } xSemaphoreGive(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters++; xSemaphoreGive(cv->mutex); _z_mutex_unlock(m); xSemaphoreTake(cv->sem, portMAX_DELAY); _z_mutex_lock(m); return _Z_RES_OK; } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } TickType_t now = xTaskGetTickCount(); TickType_t target_time = *abstime; TickType_t block_duration = (target_time > now) ? (target_time - now) : 0; xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters++; xSemaphoreGive(cv->mutex); _z_mutex_unlock(m); bool timed_out = xSemaphoreTake(cv->sem, block_duration) == pdFALSE; _z_mutex_lock(m); if (timed_out) { xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters--; xSemaphoreGive(cv->mutex); return Z_ETIMEDOUT; } return _Z_RES_OK; } #endif // Z_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { vTaskDelay(pdMS_TO_TICKS(time / 1000)); return _Z_RES_OK; } z_result_t z_sleep_ms(size_t time) { vTaskDelay(pdMS_TO_TICKS(time)); return _Z_RES_OK; } z_result_t z_sleep_s(size_t time) { vTaskDelay(pdMS_TO_TICKS(time * 1000)); return _Z_RES_OK; } /*------------------ Clock ------------------*/ z_clock_t z_clock_now(void) { return xTaskGetTickCount(); } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { return zp_clock_elapsed_ms_since(instant, epoch) * 1000; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { return *instant > *epoch ? (*instant - *epoch) * portTICK_PERIOD_MS : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { return zp_clock_elapsed_ms_since(instant, epoch) / 1000; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { return z_clock_elapsed_ms(instant) * 1000; } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now = z_clock_now(); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { return z_clock_elapsed_ms(instant) / 1000; } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { z_clock_advance_ms(clock, duration / 1000); } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { unsigned long ticks = pdMS_TO_TICKS(duration); *clock += ticks; } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { z_clock_advance_ms(clock, duration * 1000); } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(now.tv_sec - time->tv_sec); return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = (uint32_t)now.tv_sec; t->nanos = (uint32_t)now.tv_usec * 1000; return _Z_RES_OK; } ================================================ FILE: src/system/mbed/network.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/config.h" #if defined(ZENOH_MBED) #include #include #include extern "C" { #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { _ZP_UNUSED(iter); _ZP_UNUSED(timeout_ms); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on MBED port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on MBED port of Zenoh-Pico" #endif } // extern "C" #endif /* defined(ZENOH_MBED) */ ================================================ FILE: src/system/mbed/system.cpp ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_MBED) #include #include #include #include "zenoh-pico/config.h" extern "C" { #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return randLIB_get_8bit(); } uint16_t z_random_u16(void) { return randLIB_get_16bit(); } uint32_t z_random_u32(void) { return randLIB_get_32bit(); } uint64_t z_random_u64(void) { return randLIB_get_64bit(); } void z_random_fill(void *buf, size_t len) { randLIB_get_n_bytes_random(buf, len); } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return malloc(size); } void *z_realloc(void *ptr, size_t size) { return realloc(ptr, size); } void z_free(void *ptr) { free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { *task = new Thread(); mbed::Callback c = mbed::Callback(fun, arg); return ((Thread *)*task)->start(c); } z_result_t _z_task_join(_z_task_t *task) { int res = ((Thread *)*task)->join(); delete ((Thread *)*task); return res; } z_result_t _z_task_detach(_z_task_t *task) { // Not implemented _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_result_t _z_task_cancel(_z_task_t *task) { int res = ((Thread *)*task)->terminate(); delete ((Thread *)*task); return res; } void _z_task_exit(void) {} void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return ((Thread *)*task)->get_id(); } _z_task_id_t _z_task_current_id(void) { return ThisThread::get_id(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { *m = new Mutex(); return 0; } z_result_t _z_mutex_drop(_z_mutex_t *m) { delete ((Mutex *)*m); return 0; } z_result_t _z_mutex_lock(_z_mutex_t *m) { ((Mutex *)*m)->lock(); return 0; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { return (((Mutex *)*m)->trylock() == true) ? 0 : -1; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { ((Mutex *)*m)->unlock(); return 0; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { return _z_mutex_init(m); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { return _z_mutex_drop(m); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { return _z_mutex_lock(m); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { return _z_mutex_try_lock(m); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { return _z_mutex_unlock(m); } /*------------------ Condvar ------------------*/ struct condvar { Mutex mutex; Semaphore sem{0}; int waiters{0}; }; z_result_t _z_condvar_init(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } *cv = new condvar(); return 0; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } delete ((condvar *)*cv); return 0; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } auto &cond_var = *(condvar *)*cv; cond_var.mutex.lock(); if (cond_var.waiters > 0) { cond_var.sem.release(); cond_var.waiters--; } cond_var.mutex.unlock(); return _Z_RES_OK; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } auto &cond_var = *(condvar *)*cv; cond_var.mutex.lock(); while (cond_var.waiters > 0) { cond_var.sem.release(); cond_var.waiters--; } cond_var.mutex.unlock(); return _Z_RES_OK; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } auto &cond_var = *(condvar *)*cv; cond_var.mutex.lock(); cond_var.waiters++; cond_var.mutex.unlock(); _z_mutex_unlock(m); cond_var.sem.acquire(); _z_mutex_lock(m); return _Z_RES_OK; } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } auto &cond_var = *(condvar *)*cv; auto target_time = Kernel::Clock::time_point(Kernel::Clock::duration(abstime->tv_sec * 1000LL + abstime->tv_nsec / 1000000)); cond_var.mutex.lock(); cond_var.waiters++; cond_var.mutex.unlock(); _z_mutex_unlock(m); bool timed_out = cond_var.sem.try_acquire_until(target_time) == false; _z_mutex_lock(m); if (timed_out) { cond_var.mutex.lock(); cond_var.waiters--; cond_var.mutex.unlock(); return Z_ETIMEDOUT; } return _Z_RES_OK; } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { ThisThread::sleep_for(chrono::milliseconds(((time / 1000) + (time % 1000 == 0 ? 0 : 1)))); return 0; } z_result_t z_sleep_ms(size_t time) { ThisThread::sleep_for(chrono::milliseconds(time)); return 0; } z_result_t z_sleep_s(size_t time) { ThisThread::sleep_for(chrono::seconds(time)); return 0; } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { auto now = Kernel::Clock::now(); auto duration = now.time_since_epoch(); auto secs = std::chrono::duration_cast(duration); auto nanos = std::chrono::duration_cast(duration - secs); z_clock_t ts; ts.tv_sec = secs.count(); ts.tv_nsec = nanos.count(); return ts; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t* epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t* epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t* epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now = z_clock_now(); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now = z_clock_now(); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now = z_clock_now(); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = now.tv_sec; t->nanos = now.tv_usec * 1000; return 0; } } // extern "C" #endif /* defined(ZENOH_MBED) */ ================================================ FILE: src/system/rpi_pico/system.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include "FreeRTOS.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return (uint8_t)get_rand_32(); } uint16_t z_random_u16(void) { return (uint16_t)get_rand_32(); } uint32_t z_random_u32(void) { return get_rand_32(); } uint64_t z_random_u64(void) { return get_rand_64(); } void z_random_fill(void *buf, size_t len) { for (size_t i = 0; i < len; i++) { *((uint8_t *)buf) = z_random_u8(); } } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { if (size == 0) { return NULL; } return pvPortMalloc(size); } void *z_realloc(void *ptr, size_t size) { _ZP_UNUSED(ptr); _ZP_UNUSED(size); // realloc not implemented in FreeRTOS assert(false); return NULL; } void z_free(void *ptr) { vPortFree(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 // In FreeRTOS, tasks created using xTaskCreate must end with vTaskDelete. // A task function should __not__ simply return. typedef struct { void *(*fun)(void *); void *arg; EventGroupHandle_t join_event; } z_task_arg; static void z_task_wrapper(void *arg) { z_task_arg *targ = (z_task_arg *)arg; targ->fun(targ->arg); xEventGroupSetBits(targ->join_event, 1); vTaskDelete(NULL); } static z_task_attr_t z_default_task_attr = { .name = "", .priority = configMAX_PRIORITIES / 2, .stack_depth = 1024, }; /*------------------ Thread ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { z_task_arg *z_arg = (z_task_arg *)z_malloc(sizeof(z_task_arg)); if (z_arg == NULL) { return -1; } z_arg->fun = fun; z_arg->arg = arg; z_arg->join_event = task->join_event = xEventGroupCreate(); if (attr == NULL) { attr = &z_default_task_attr; } if (xTaskCreate(z_task_wrapper, attr->name, attr->stack_depth, z_arg, attr->priority, &task->handle) != pdPASS) { return -1; } return 0; } z_result_t _z_task_join(_z_task_t *task) { xEventGroupWaitBits(task->join_event, 1, pdFALSE, pdFALSE, portMAX_DELAY); return 0; } z_result_t _z_task_detach(_z_task_t *task) { _ZP_UNUSED(task); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_result_t _z_task_cancel(_z_task_t *task) { vTaskDelete(task->handle); return 0; } void _z_task_exit(void) { vTaskDelete(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr->join_event); z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return task->handle; } _z_task_id_t _z_task_current_id(void) { return xTaskGetCurrentTaskHandle(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { *m = xSemaphoreCreateRecursiveMutex(); return *m == NULL ? -1 : 0; } z_result_t _z_mutex_drop(_z_mutex_t *m) { z_free(*m); return 0; } z_result_t _z_mutex_lock(_z_mutex_t *m) { return xSemaphoreTakeRecursive(*m, portMAX_DELAY) == pdTRUE ? 0 : -1; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { return xSemaphoreTakeRecursive(*m, 0) == pdTRUE ? 0 : -1; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { return xSemaphoreGiveRecursive(*m) == pdTRUE ? 0 : -1; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { return _z_mutex_init(m); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { return _z_mutex_drop(m); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { return _z_mutex_lock(m); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { return _z_mutex_try_lock(m); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { return _z_mutex_unlock(m); } /*------------------ CondVar ------------------*/ static UBaseType_t CONDVAR_MAX_WAITERS_COUNT = UINT_MAX; z_result_t _z_condvar_init(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } cv->mutex = xSemaphoreCreateMutex(); cv->sem = xSemaphoreCreateCounting(CONDVAR_MAX_WAITERS_COUNT, 0); cv->waiters = 0; if (!cv->mutex || !cv->sem) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } vSemaphoreDelete(cv->sem); vSemaphoreDelete(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); if (cv->waiters > 0) { xSemaphoreGive(cv->sem); cv->waiters--; } xSemaphoreGive(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); while (cv->waiters > 0) { xSemaphoreGive(cv->sem); cv->waiters--; } xSemaphoreGive(cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters++; xSemaphoreGive(cv->mutex); _z_mutex_unlock(m); xSemaphoreTake(cv->sem, portMAX_DELAY); return _z_mutex_lock(m); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } TickType_t now = xTaskGetTickCount(); TickType_t target_time = pdMS_TO_TICKS(abstime->tv_sec * 1000 + abstime->tv_nsec / 1000000); TickType_t block_duration = (target_time > now) ? (target_time - now) : 0; xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters++; xSemaphoreGive(cv->mutex); _z_mutex_unlock(m); bool timed_out = xSemaphoreTake(cv->sem, block_duration) == pdFALSE; _z_mutex_lock(m); if (timed_out) { xSemaphoreTake(cv->mutex, portMAX_DELAY); cv->waiters--; xSemaphoreGive(cv->mutex); return Z_ETIMEDOUT; } return _Z_RES_OK; } #endif // Z_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { vTaskDelay(pdMS_TO_TICKS(time / 1000)); return 0; } z_result_t z_sleep_ms(size_t time) { vTaskDelay(pdMS_TO_TICKS(time)); return 0; } z_result_t z_sleep_s(size_t time) { vTaskDelay(pdMS_TO_TICKS(time * 1000)); return 0; } /*------------------ Clock ------------------*/ void __z_clock_gettime(z_clock_t *ts) { uint64_t m = xTaskGetTickCount() / portTICK_PERIOD_MS; ts->tv_sec = m / (uint64_t)1000; ts->tv_nsec = (m % (uint64_t)1000) * (uint64_t)1000000; } z_clock_t z_clock_now(void) { z_clock_t now; __z_clock_gettime(&now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(now.tv_sec - time->tv_sec); return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = (uint32_t)now.tv_sec; t->nanos = (uint32_t)now.tv_usec * 1000; return 0; } ================================================ FILE: src/system/rpi_pico/usb_uart.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_LINK_SERIAL == 1 && Z_FEATURE_LINK_SERIAL_USB == 1 #include "pico/unique_id.h" #include "tusb.h" // ===== USB descriptors ===== // Copied from // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_stdio_usb/stdio_usb_descriptors.c // #ifndef USBD_VID #define USBD_VID (0x2E8A) // Raspberry Pi #endif #ifndef USBD_PID #if PICO_RP2040 #define USBD_PID (0x000a) // Raspberry Pi Pico SDK CDC for RP2040 #else #define USBD_PID (0x0009) // Raspberry Pi Pico SDK CDC #endif #endif #ifndef USBD_MANUFACTURER #define USBD_MANUFACTURER "Raspberry Pi" #endif #ifndef USBD_PRODUCT #define USBD_PRODUCT "Pico" #endif #define TUD_RPI_RESET_DESC_LEN 9 #if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) #else #define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_RPI_RESET_DESC_LEN) #endif #if !PICO_STDIO_USB_DEVICE_SELF_POWERED #define USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE (0) #define USBD_MAX_POWER_MA (250) #else #define USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE TUSB_DESC_CONFIG_ATT_SELF_POWERED #define USBD_MAX_POWER_MA (1) #endif #define USBD_ITF_CDC (0) // needs 2 interfaces #if !PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE #define USBD_ITF_MAX (2) #else #define USBD_ITF_RPI_RESET (2) #define USBD_ITF_MAX (3) #endif #define USBD_CDC_EP_CMD (0x81) #define USBD_CDC_EP_OUT (0x02) #define USBD_CDC_EP_IN (0x82) #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE (64) #define USBD_STR_0 (0x00) #define USBD_STR_MANUF (0x01) #define USBD_STR_PRODUCT (0x02) #define USBD_STR_SERIAL (0x03) #define USBD_STR_CDC (0x04) #define USBD_STR_RPI_RESET (0x05) // Note: descriptors returned from callbacks must exist long enough for transfer to complete static const tusb_desc_device_t usbd_desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, // On Windows, if bcdUSB = 0x210 then a Microsoft OS 2.0 descriptor is required, else the device won't be detected // This is only needed for driverless access to the reset interface - the CDC interface doesn't require these // descriptors for driverless access, but will still not work if bcdUSB = 0x210 and no descriptor is provided. Therefore // always use bcdUSB = 0x200 if the Microsoft OS 2.0 descriptor isn't enabled #if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE && PICO_STDIO_USB_RESET_INTERFACE_SUPPORT_MS_OS_20_DESCRIPTOR .bcdUSB = 0x0210, #else .bcdUSB = 0x0200, #endif .bDeviceClass = TUSB_CLASS_MISC, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, .idVendor = USBD_VID, .idProduct = USBD_PID, .bcdDevice = 0x0100, .iManufacturer = USBD_STR_MANUF, .iProduct = USBD_STR_PRODUCT, .iSerialNumber = USBD_STR_SERIAL, .bNumConfigurations = 1, }; #define TUD_RPI_RESET_DESCRIPTOR(_itfnum, _stridx) \ /* Interface */ \ 9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUSB_CLASS_VENDOR_SPECIFIC, RESET_INTERFACE_SUBCLASS, \ RESET_INTERFACE_PROTOCOL, _stridx, static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, USBD_CONFIGURATION_DESCRIPTOR_ATTRIBUTE, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), #if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE TUD_RPI_RESET_DESCRIPTOR(USBD_ITF_RPI_RESET, USBD_STR_RPI_RESET) #endif }; static char usbd_serial_str[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; static const char *const usbd_desc_str[] = { [USBD_STR_MANUF] = USBD_MANUFACTURER, [USBD_STR_PRODUCT] = USBD_PRODUCT, [USBD_STR_SERIAL] = usbd_serial_str, [USBD_STR_CDC] = "Board CDC", #if PICO_STDIO_USB_ENABLE_RESET_VIA_VENDOR_INTERFACE [USBD_STR_RPI_RESET] = "Reset", #endif }; const uint8_t *tud_descriptor_device_cb(void) { return (const uint8_t *)&usbd_desc_device; } const uint8_t *tud_descriptor_configuration_cb(__unused uint8_t index) { return usbd_desc_cfg; } const uint16_t *tud_descriptor_string_cb(uint8_t index, __unused uint16_t langid) { #ifndef USBD_DESC_STR_MAX #define USBD_DESC_STR_MAX (20) #elif USBD_DESC_STR_MAX > 127 #error USBD_DESC_STR_MAX too high (max is 127). #elif USBD_DESC_STR_MAX < 17 #error USBD_DESC_STR_MAX too low (min is 17). #endif static uint16_t desc_str[USBD_DESC_STR_MAX]; // Assign the SN using the unique flash id if (!usbd_serial_str[0]) { pico_get_unique_board_id_string(usbd_serial_str, sizeof(usbd_serial_str)); } uint8_t len; if (index == 0) { desc_str[1] = 0x0409; // supported language is English len = 1; } else { if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { return NULL; } const char *str = usbd_desc_str[index]; for (len = 0; len < USBD_DESC_STR_MAX - 1 && str[len]; ++len) { desc_str[1 + len] = str[len]; } } // first byte is length (including header), second byte is string type desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * len + 2)); return desc_str; } // ===== USB UART methods ===== static _z_mutex_t _z_usb_uart_mutex; static _z_task_t _z_usb_uart_task; static volatile bool _z_usb_uart_task_run; static void *_z_usb_uart_task_proc(void *) { while (_z_usb_uart_task_run) { _z_mutex_lock(&_z_usb_uart_mutex); tud_task(); _z_mutex_unlock(&_z_usb_uart_mutex); z_sleep_ms(1); } return NULL; } void _z_usb_uart_init() { tud_init(BOARD_TUD_RHPORT); _z_mutex_init(&_z_usb_uart_mutex); _z_usb_uart_task_run = true; _z_task_init(&_z_usb_uart_task, NULL, &_z_usb_uart_task_proc, NULL); _Z_DEBUG("whating for host..."); while (!tud_cdc_connected()) { z_sleep_ms(100); } } void _z_usb_uart_deinit() { _z_usb_uart_task_run = false; _z_task_join(&_z_usb_uart_task); _z_mutex_drop(&_z_usb_uart_mutex); tud_deinit(BOARD_TUD_RHPORT); } void _z_usb_uart_write(const uint8_t *buf, int length) { _z_mutex_lock(&_z_usb_uart_mutex); for (int i = 0; i < length;) { int n = length - i; int avail = (int)tud_cdc_write_available(); if (n > avail) { n = avail; } if (n != 0) { int n2 = (int)tud_cdc_write(buf + i, (uint32_t)n); tud_task(); tud_cdc_write_flush(); i += n2; } else { tud_task(); tud_cdc_write_flush(); } } _z_mutex_unlock(&_z_usb_uart_mutex); } uint8_t _z_usb_uart_getc() { uint8_t ret = 0; bool ready = false; while (true) { _z_mutex_lock(&_z_usb_uart_mutex); ready = tud_cdc_available(); if (ready) { ret = tud_cdc_read_char(); } _z_mutex_unlock(&_z_usb_uart_mutex); if (ready) { break; } else { z_sleep_ms(1); } } return ret; } #endif ================================================ FILE: src/system/socket/esp32.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #if defined(ZP_PLATFORM_SOCKET_ESP32) #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) #include #include #include #include #include #include #endif #include "zenoh-pico/utils/logging.h" #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { int flags = fcntl(sock->_fd, F_GETFL, 0); if (flags == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (fcntl(sock->_fd, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)) == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { shutdown(sock->_fd, SHUT_RDWR); close(sock->_fd); sock->_fd = -1; } } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { fd_set read_fds; int max_fd = 0; bool has_sockets = false; FD_ZERO(&read_fds); _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, false); FD_SET(sock->_fd, &read_fds); if (sock->_fd > max_fd) { max_fd = sock->_fd; } has_sockets = true; } if (!has_sockets) { return _Z_RES_OK; } struct timeval timeout = { .tv_sec = (time_t)(timeout_ms / 1000U), .tv_usec = (suseconds_t)((timeout_ms % 1000U) * 1000U), }; if (select(max_fd + 1, &read_fds, NULL, NULL, &timeout) <= 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FD_ISSET(sock->_fd, &read_fds)); } return _Z_RES_OK; } #else z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { _ZP_UNUSED(iter); _ZP_UNUSED(timeout_ms); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif #endif /* defined(ZP_PLATFORM_SOCKET_ESP32) */ ================================================ FILE: src/system/socket/lwip.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/socket.h" #if defined(ZP_PLATFORM_SOCKET_LWIP) #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) #include #include "lwip/sockets.h" #include "zenoh-pico/link/transport/lwip_socket.h" #endif #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #if defined(ZP_PLATFORM_SOCKET_LINKS_ENABLED) && Z_FEATURE_LINK_TCP == 1 z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { int fd = _z_lwip_socket_get(*sock); int flags = lwip_fcntl(fd, F_GETFL, 0); if (flags == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (lwip_fcntl(fd, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)) == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { int fd = _z_lwip_socket_get(*sock); if (fd >= 0) { shutdown(fd, SHUT_RDWR); lwip_close(fd); _z_lwip_socket_set(sock, -1); } } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { fd_set read_fds; int max_fd = 0; bool has_sockets = false; FD_ZERO(&read_fds); _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); int fd = _z_lwip_socket_get(*sock); _z_socket_wait_iter_set_ready(iter, false); FD_SET(fd, &read_fds); if (fd > max_fd) { max_fd = fd; } has_sockets = true; } if (!has_sockets) { return _Z_RES_OK; } struct timeval timeout = { .tv_sec = (time_t)(timeout_ms / 1000U), .tv_usec = (suseconds_t)((timeout_ms % 1000U) * 1000U), }; if (lwip_select(max_fd + 1, &read_fds, NULL, NULL, &timeout) <= 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FD_ISSET(_z_lwip_socket_get(*sock), &read_fds)); } return _Z_RES_OK; } #else z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { _ZP_UNUSED(sock); _ZP_UNUSED(blocking); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { _ZP_UNUSED(iter); _ZP_UNUSED(timeout_ms); _Z_ERROR("Function not yet supported on this system"); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } #endif #endif /* defined(ZP_PLATFORM_SOCKET_LWIP) */ ================================================ FILE: src/system/threadx/stm32/network.c ================================================ /* Ubiquity robotics * ====================================================================== * Zenoh-pico stm32 threadx * Network implementation for serial device running in circular DMA mode. * ====================================================================== */ #if defined(ZENOH_THREADX_STM32) #include "hal.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/pointers.h" #if Z_FEATURE_LINK_TCP == 1 #error "Z_FEATURE_LINK_TCP is not supported" #endif #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Z_FEATURE_LINK_BLUETOOTH is not supported" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Z_FEATURE_RAWETH_TRANSPORT is not supported" #endif void _z_socket_close(_z_sys_net_socket_t *sock) { _ZP_UNUSED(sock); } #endif // ZENOH_THREADX_STM32 ================================================ FILE: src/system/threadx/stm32/system.c ================================================ /* Ubiquity robotics * ============================================================= * Zenoh-pico stm32 threadx * System implementation for threadx. * ============================================================= */ #if defined(ZENOH_THREADX_STM32) #include #include #include #include #include "tx_api.h" #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" /* pointer to threadx byte pool, should be provided by application */ extern TX_BYTE_POOL *pthreadx_byte_pool; /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return z_random_u32(); } uint16_t z_random_u16(void) { return z_random_u32(); } uint32_t z_random_u32(void) { return random(); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { for (size_t i = 0; i < len; i++) { ((uint8_t *)buf)[i] = z_random_u8(); } } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { void *ptr = NULL; uint8_t r = tx_byte_allocate(pthreadx_byte_pool, &ptr, size, TX_WAIT_FOREVER); if (r != TX_SUCCESS) { ptr = NULL; } return ptr; } void *z_realloc(void *ptr, size_t size) { // realloc not implemented return NULL; } void z_free(void *ptr) { tx_byte_release(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Thread ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { _Z_DEBUG("Creating a new task!"); UINT status = tx_thread_create(&(task->threadx_thread), "ztask", (VOID(*)(ULONG))fun, (ULONG)arg, task->threadx_stack, Z_TASK_STACK_SIZE, Z_TASK_PRIORITY, Z_TASK_PREEMPT_THRESHOLD, Z_TASK_TIME_SLICE, TX_AUTO_START); if (status != TX_SUCCESS) _Z_ERROR_RETURN(_Z_ERR_GENERIC); return _Z_RES_OK; } z_result_t _z_task_join(_z_task_t *task) { while (1) { UINT state; UINT status = tx_thread_info_get(&(task->threadx_thread), NULL, &state, NULL, NULL, NULL, NULL, NULL, NULL); if (status != TX_SUCCESS) _Z_ERROR_RETURN(_Z_ERR_GENERIC); if ((state == TX_COMPLETED) || (state == TX_TERMINATED)) break; tx_thread_sleep(1); } return _Z_RES_OK; } z_result_t _z_task_detach(_z_task_t *task) { // Not implemented _Z_ERROR_RETURN(_Z_ERR_GENERIC); } z_result_t _z_task_cancel(_z_task_t *task) { // Not implemented _Z_ERROR_RETURN(_Z_ERR_GENERIC); } void _z_task_exit(void) { // NEW with new vesion // Not implemented } void _z_task_free(_z_task_t **task) { z_free(*task); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return (TX_THREAD *)&(task->threadx_thread); } _z_task_id_t _z_task_current_id(void) { return tx_thread_identify(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { UINT status = tx_mutex_create(m, TX_NULL, TX_INHERIT); if (status == TX_MUTEX_ERROR) { // zenoh-pico reuses mutex if zenoh_init() fails. status = tx_mutex_delete(m); if (status == TX_SUCCESS) { status = tx_mutex_create(m, TX_NULL, TX_INHERIT); } } return (status == TX_SUCCESS) ? 0 : -1; } z_result_t _z_mutex_drop(_z_mutex_t *m) { UINT status = tx_mutex_delete(m); return (status == TX_SUCCESS) ? 0 : -1; } z_result_t _z_mutex_lock(_z_mutex_t *m) { UINT status = tx_mutex_get(m, TX_WAIT_FOREVER); return (status == TX_SUCCESS) ? 0 : -1; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { UINT status = tx_mutex_get(m, TX_NO_WAIT); // Return immediately even if the mutex was not available return (status == TX_SUCCESS) ? 0 : -1; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { UINT status = tx_mutex_put(m); return (status == TX_SUCCESS) ? 0 : -1; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { return _z_mutex_init(m); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { return _z_mutex_drop(m); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { return _z_mutex_lock(m); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { return _z_mutex_unlock(m); } /*------------------ CondVar ------------------*/ #define CONDVAR_MAX_WAITERS_COUNT 0xff; z_result_t _z_condvar_init(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } UINT m_status = tx_mutex_create(&cv->mutex, TX_NULL, TX_INHERIT); UINT s_status = tx_semaphore_create(&cv->sem, TX_NULL, 0); cv->waiters = 0; if (m_status != TX_SUCCESS || s_status != TX_SUCCESS) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } tx_mutex_delete(&cv->mutex); tx_semaphore_delete(&cv->sem); return _Z_RES_OK; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } tx_mutex_get(&cv->mutex, TX_WAIT_FOREVER); if (cv->waiters > 0) { tx_semaphore_put(&cv->sem); cv->waiters--; } tx_mutex_put(&cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { if (!cv) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } tx_mutex_get(&cv->mutex, TX_WAIT_FOREVER); while (cv->waiters > 0) { tx_semaphore_put(&cv->sem); cv->waiters--; } tx_mutex_put(&cv->mutex); return _Z_RES_OK; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } tx_mutex_get(&cv->mutex, TX_WAIT_FOREVER); cv->waiters++; tx_mutex_put(&cv->mutex); _z_mutex_unlock(m); tx_semaphore_get(&cv->sem, TX_WAIT_FOREVER); _z_mutex_lock(m); return _Z_RES_OK; } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { if (!cv || !m) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } ULONG now = tx_time_get(); ULONG target_time = (abstime->tv_sec * 1000 + abstime->tv_nsec / 1000000) * (TX_TIMER_TICKS_PER_SECOND / 1000); ULONG block_duration = (target_time > now) ? (target_time - now) : 0; tx_mutex_get(&cv->mutex, TX_WAIT_FOREVER); cv->waiters++; tx_mutex_put(&cv->mutex); _z_mutex_unlock(m); bool timed_out = tx_semaphore_get(&cv->sem, block_duration) != TX_SUCCESS; _z_mutex_lock(m); if (timed_out) { tx_mutex_get(&cv->mutex, TX_WAIT_FOREVER); cv->waiters--; tx_mutex_put(&cv->mutex); return Z_ETIMEDOUT; } return _Z_RES_OK; } #endif // Z_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { tx_thread_sleep(time * TX_TIMER_TICKS_PER_SECOND / 1000000); return 0; } z_result_t z_sleep_ms(size_t time) { tx_thread_sleep(time * TX_TIMER_TICKS_PER_SECOND / 1000); return 0; } z_result_t z_sleep_s(size_t time) { tx_thread_sleep(time * TX_TIMER_TICKS_PER_SECOND); return 0; } /*------------------ Clock ------------------*/ void __z_clock_gettime(z_clock_t *ts) { uint64_t ms = tx_time_get() * (TX_TIMER_TICKS_PER_SECOND / 1000); ts->tv_sec = ms / (uint64_t)1000; ts->tv_nsec = (ms % (uint64_t)1000) * (uint64_t)1000; } z_clock_t z_clock_now(void) { z_clock_t now; __z_clock_gettime(&now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; __z_clock_gettime(&now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { return tx_time_get(); } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { snprintf(buf, buflen, "%lu", z_time_now()); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { return (tx_time_get() - *time) * 1000000ULL / TX_TIMER_TICKS_PER_SECOND; } unsigned long z_time_elapsed_ms(z_time_t *time) { return (tx_time_get() - *time) * 1000ULL / TX_TIMER_TICKS_PER_SECOND; } unsigned long z_time_elapsed_s(z_time_t *time) { return (tx_time_get() - *time) * TX_TIMER_TICKS_PER_SECOND; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { ULONG64 time_ns = tx_time_get() * 1000000000ULL / TX_TIMER_TICKS_PER_SECOND; t->secs = time_ns / 1000000000ULL; t->nanos = time_ns % 1000000000ULL; return _Z_RES_OK; } #endif // ZENOH_THREADX_STM32 ================================================ FILE: src/system/unix/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { int flags = fcntl(sock->_fd, F_GETFL, 0); if (flags == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (fcntl(sock->_fd, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)) == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { shutdown(sock->_fd, SHUT_RDWR); close(sock->_fd); sock->_fd = -1; } } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { fd_set read_fds; int max_fd = 0; bool has_sockets = false; FD_ZERO(&read_fds); _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, false); FD_SET(sock->_fd, &read_fds); if (sock->_fd > max_fd) { max_fd = sock->_fd; } has_sockets = true; } if (!has_sockets) { return _Z_RES_OK; } struct timeval timeout = { .tv_sec = (time_t)(timeout_ms / 1000U), .tv_usec = (suseconds_t)((timeout_ms % 1000U) * 1000U), }; int result = select(max_fd + 1, &read_fds, NULL, NULL, &timeout); if (result < 0) { _Z_DEBUG("Errno: %d\n", errno); _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FD_ISSET(sock->_fd, &read_fds)); } return _Z_RES_OK; } #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on Unix port of Zenoh-Pico" #endif ================================================ FILE: src/system/unix/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "zenoh-pico/utils/result.h" #if defined(ZENOH_LINUX) #include #include #endif #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/system_error.h" #include "zenoh-pico/system/platform.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { uint8_t ret = 0; #if defined(ZENOH_LINUX) while (getrandom(&ret, sizeof(uint8_t), 0) <= 0) { ZP_ASM_NOP; } #elif defined(ZENOH_MACOS) || defined(ZENOH_BSD) ret = z_random_u32(); #endif return ret; } uint16_t z_random_u16(void) { uint16_t ret = 0; #if defined(ZENOH_LINUX) while (getrandom(&ret, sizeof(uint16_t), 0) <= 0) { ZP_ASM_NOP; } #elif defined(ZENOH_MACOS) || defined(ZENOH_BSD) ret = z_random_u32(); #endif return ret; } uint32_t z_random_u32(void) { uint32_t ret = 0; #if defined(ZENOH_LINUX) while (getrandom(&ret, sizeof(uint32_t), 0) <= 0) { ZP_ASM_NOP; } #elif defined(ZENOH_MACOS) || defined(ZENOH_BSD) ret = arc4random(); #endif return ret; } uint64_t z_random_u64(void) { uint64_t ret = 0; #if defined(ZENOH_LINUX) while (getrandom(&ret, sizeof(uint64_t), 0) <= 0) { ZP_ASM_NOP; } #elif defined(ZENOH_MACOS) || defined(ZENOH_BSD) ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); #endif return ret; } void z_random_fill(void *buf, size_t len) { #if defined(ZENOH_LINUX) while (getrandom(buf, len, 0) <= 0) { ZP_ASM_NOP; } #elif defined(ZENOH_MACOS) || defined(ZENOH_BSD) arc4random_buf(buf, len); #endif } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return malloc(size); } void *z_realloc(void *ptr, size_t size) { return realloc(ptr, size); } void z_free(void *ptr) { free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { _Z_CHECK_SYS_ERR(pthread_create(task, attr, fun, arg)); } z_result_t _z_task_join(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_join(*task, NULL)); } z_result_t _z_task_detach(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_detach(*task)); } z_result_t _z_task_cancel(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_cancel(*task)); } void _z_task_exit(void) { pthread_exit(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return *task; } _z_task_id_t _z_task_current_id(void) { return pthread_self(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return pthread_equal(*l, *r) != 0; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_init(m, NULL)); } z_result_t _z_mutex_drop(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_unlock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { pthread_mutexattr_t attr; _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_init(&attr)); _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); _Z_RETURN_IF_SYS_ERR(pthread_mutex_init(m, &attr)); _Z_CHECK_SYS_ERR(pthread_mutexattr_destroy(&attr)); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { pthread_condattr_t attr; pthread_condattr_init(&attr); #ifndef ZENOH_MACOS // macos does not have pthread_condattr_setclock function pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); #endif _Z_CHECK_SYS_ERR(pthread_cond_init(cv, &attr)); } z_result_t _z_condvar_drop(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_destroy(cv)); } z_result_t _z_condvar_signal(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_signal(cv)); } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_broadcast(cv)); } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_cond_wait(cv, m)); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { #ifndef ZENOH_MACOS int error = pthread_cond_timedwait(cv, m, abstime); #else // pthread_cond_timedwait does not work for macos since it assumes REALTIME_CLOCK // while z_clock_t corresponds to MONOTONIC_CLOCK. z_clock_t deadline = {0}; z_clock_t now = z_clock_now(); if (now.tv_sec < abstime->tv_sec) { deadline.tv_sec = abstime->tv_sec - now.tv_sec; if (now.tv_nsec <= abstime->tv_nsec) { deadline.tv_nsec = abstime->tv_nsec - now.tv_nsec; } else { deadline.tv_sec--; deadline.tv_nsec += 1000000000 + abstime->tv_nsec - now.tv_nsec; } } else if (now.tv_sec == abstime->tv_sec && now.tv_nsec < abstime->tv_nsec) { deadline.tv_nsec = abstime->tv_nsec - now.tv_nsec; } int error = pthread_cond_timedwait_relative_np(cv, m, &deadline); #endif if (error == ETIMEDOUT) { return Z_ETIMEDOUT; } _Z_CHECK_SYS_ERR(error); } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { _Z_CHECK_SYS_ERR(usleep((unsigned int)time)); } z_result_t z_sleep_ms(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_ms(&start) < time) { z_result_t ret = z_sleep_us(1000); if (ret != _Z_RES_OK) { return ret; } } return _Z_RES_OK; } z_result_t z_sleep_s(size_t time) { _Z_CHECK_SYS_ERR((int)sleep((unsigned int)time)); } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += (time_t)(duration / 1000000); clock->tv_nsec += (long int)((duration % 1000000) * 1000); if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += (time_t)(duration / 1000); clock->tv_nsec += (long int)((duration % 1000) * 1000000); if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += (time_t)duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (unsigned long)(now.tv_sec - time->tv_sec); return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = (uint32_t)now.tv_sec; t->nanos = (uint32_t)now.tv_usec * 1000; return 0; } ================================================ FILE: src/system/windows/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include // The following includes must come after winsock2 #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" WSADATA wsaData; z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { u_long mode = blocking ? 0 : 1; if (ioctlsocket(sock->_sock._fd, FIONBIO, &mode) != 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { if (sock->_sock._fd != INVALID_SOCKET) { shutdown(sock->_sock._fd, SD_BOTH); closesocket(sock->_sock._fd); sock->_sock._fd = INVALID_SOCKET; } } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { fd_set read_fds; bool has_sockets = false; FD_ZERO(&read_fds); _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, false); FD_SET(sock->_sock._fd, &read_fds); has_sockets = true; } if (!has_sockets) { return _Z_RES_OK; } struct timeval timeout = { .tv_sec = (long)(timeout_ms / 1000U), .tv_usec = (long)((timeout_ms % 1000U) * 1000U), }; int result = select(0, &read_fds, NULL, NULL, &timeout); if (result <= 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FD_ISSET(sock->_sock._fd, &read_fds)); } return _Z_RES_OK; } #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on Windows port of Zenoh-Pico" #endif #if Z_FEATURE_LINK_SERIAL == 1 #error "Serial not supported yet on Windows port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on Windows port of Zenoh-Pico" #endif ================================================ FILE: src/system/windows/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include #include // The following includes must come after windows #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { uint8_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } uint16_t z_random_u16(void) { uint16_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } uint32_t z_random_u32(void) { uint32_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } uint64_t z_random_u64(void) { uint64_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } void z_random_fill(void *buf, size_t len) { RtlGenRandom(buf, (unsigned long)len); } /*------------------ Memory ------------------*/ // #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) // #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) void *z_malloc(size_t size) { return malloc(size); } void *z_realloc(void *ptr, size_t size) { return realloc(ptr, size); } void z_free(void *ptr) { free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { (void)(attr); z_result_t ret = _Z_RES_OK; *task = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fun, arg, 0, NULL); if (*task == NULL) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_TASK_FAILED); ret = _Z_ERR_SYSTEM_TASK_FAILED; } return ret; } z_result_t _z_task_join(_z_task_t *task) { z_result_t ret = _Z_RES_OK; WaitForSingleObject(*task, INFINITE); return ret; } z_result_t _z_task_detach(_z_task_t *task) { z_result_t ret = _Z_RES_OK; CloseHandle(*task); *task = 0; return ret; } z_result_t _z_task_cancel(_z_task_t *task) { z_result_t ret = _Z_RES_OK; TerminateThread(*task, 0); return ret; } void _z_task_exit(void) { ExitThread(0); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; if (*ptr != 0) { CloseHandle(*ptr); } z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return GetThreadId(*task); } _z_task_id_t _z_task_current_id(void) { return GetCurrentThreadId(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return *l == *r; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { z_result_t ret = _Z_RES_OK; InitializeSRWLock(m); return ret; } z_result_t _z_mutex_drop(_z_mutex_t *m) { (void)(m); z_result_t ret = _Z_RES_OK; return ret; } z_result_t _z_mutex_lock(_z_mutex_t *m) { z_result_t ret = _Z_RES_OK; AcquireSRWLockExclusive(m); return ret; } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { z_result_t ret = _Z_RES_OK; if (!TryAcquireSRWLockExclusive(m)) { _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } return ret; } z_result_t _z_mutex_unlock(_z_mutex_t *m) { z_result_t ret = _Z_RES_OK; ReleaseSRWLockExclusive(m); return ret; } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { InitializeCriticalSection(m); return _Z_RES_OK; } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { DeleteCriticalSection(m); return _Z_RES_OK; } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { EnterCriticalSection(m); return _Z_RES_OK; } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { if (!TryEnterCriticalSection(m)) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { LeaveCriticalSection(m); return _Z_RES_OK; } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { z_result_t ret = _Z_RES_OK; InitializeConditionVariable(cv); return ret; } z_result_t _z_condvar_drop(_z_condvar_t *cv) { (void)(cv); z_result_t ret = _Z_RES_OK; return ret; } z_result_t _z_condvar_signal(_z_condvar_t *cv) { z_result_t ret = _Z_RES_OK; WakeConditionVariable(cv); return ret; } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { z_result_t ret = _Z_RES_OK; WakeAllConditionVariable(cv); return ret; } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { z_result_t ret = _Z_RES_OK; SleepConditionVariableSRW(cv, m, INFINITE, 0); return ret; } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { z_clock_t now = z_clock_now(); LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } double remaining = (double)(abstime->QuadPart - now.QuadPart) / frequency.QuadPart * 1000.0; DWORD block_duration = remaining > 0.0 ? (DWORD)remaining : 0; if (SleepConditionVariableSRW(cv, m, block_duration, 0) == 0) { if (GetLastError() == ERROR_TIMEOUT) { return Z_ETIMEDOUT; } else { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } } return _Z_RES_OK; } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { return z_sleep_ms((time / 1000) + (time % 1000 == 0 ? 0 : 1)); } z_result_t z_sleep_ms(size_t time) { // Guarantees that size_t is split into DWORD segments for Sleep uint8_t ratio = sizeof(size_t) / sizeof(DWORD); DWORD ratio_time = (DWORD)((time / ratio) + (time % ratio == 0 ? 0 : 1)); for (uint8_t i = 0; i < ratio; i++) { Sleep(ratio_time); } return 0; } z_result_t z_sleep_s(size_t time) { z_time_t start = z_time_now(); // Most sleep APIs promise to sleep at least whatever you asked them to. // This may compound, so this approach may make sleeps longer than expected. // This extra check tries to minimize the amount of extra time it might sleep. while (z_time_elapsed_s(&start) < time) { z_sleep_ms(1000); } return 0; } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { z_clock_t now; QueryPerformanceCounter(&now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return 0; } double elapsed = (double)(instant->QuadPart - epoch->QuadPart) * 1000000.0; elapsed /= frequency.QuadPart; return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return 0; } double elapsed = (double)(instant->QuadPart - epoch->QuadPart) * 1000.0; elapsed /= frequency.QuadPart; return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return 0; } double elapsed = (double)(instant->QuadPart - epoch->QuadPart) / frequency.QuadPart; return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; QueryPerformanceCounter(&now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; QueryPerformanceCounter(&now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; QueryPerformanceCounter(&now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return; } double ticks = (double)duration * frequency.QuadPart / 1000000.0; clock->QuadPart += (LONGLONG)ticks; } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return; } double ticks = (double)duration * frequency.QuadPart / 1000.0; clock->QuadPart += (LONGLONG)ticks; } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); // ticks per second // Hardware not supporting QueryPerformanceFrequency if (frequency.QuadPart == 0) { return; } double ticks = (double)duration * frequency.QuadPart; clock->QuadPart += (LONGLONG)ticks; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; ftime(&now); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.time); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { return z_time_elapsed_ms(time) * 1000; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; ftime(&now); unsigned long elapsed = ((unsigned long)(now.time - time->time) * 1000) + (now.millitm - time->millitm); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; ftime(&now); unsigned long elapsed = (unsigned long)(now.time - time->time); return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; ftime(&now); t->secs = (uint32_t)now.time; t->nanos = (uint32_t)(now.millitm * 1000000); return 0; } ================================================ FILE: src/system/zephyr/network.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/config.h" #if defined(ZENOH_ZEPHYR) #include #include #include #include #include #include #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" z_result_t _z_socket_set_blocking(const _z_sys_net_socket_t *sock, bool blocking) { int flags = fcntl(sock->_fd, F_GETFL, 0); if (flags == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (fcntl(sock->_fd, F_SETFL, blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK)) == -1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } return _Z_RES_OK; } void _z_socket_close(_z_sys_net_socket_t *sock) { if (sock->_fd >= 0) { close(sock->_fd); sock->_fd = -1; } } z_result_t _z_socket_wait_readable(_z_socket_wait_iter_t *iter, uint32_t timeout_ms) { fd_set read_fds; int max_fd = 0; bool has_sockets = false; FD_ZERO(&read_fds); _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, false); FD_SET(sock->_fd, &read_fds); if (sock->_fd > max_fd) { max_fd = sock->_fd; } has_sockets = true; } if (!has_sockets) { return _Z_RES_OK; } struct timeval timeout = { .tv_sec = (time_t)(timeout_ms / 1000U), .tv_usec = (suseconds_t)((timeout_ms % 1000U) * 1000U), }; int result = select(max_fd + 1, &read_fds, NULL, NULL, &timeout); if (result <= 0) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_socket_wait_iter_reset(iter); while (_z_socket_wait_iter_next(iter)) { const _z_sys_net_socket_t *sock = _z_socket_wait_iter_get_socket(iter); _z_socket_wait_iter_set_ready(iter, FD_ISSET(sock->_fd, &read_fds)); } return _Z_RES_OK; } #if Z_FEATURE_LINK_BLUETOOTH == 1 #error "Bluetooth not supported yet on Zephyr port of Zenoh-Pico" #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 #error "Raw ethernet transport not supported yet on Zephyr port of Zenoh-Pico" #endif #endif /* defined(ZENOH_ZEPHYR) */ ================================================ FILE: src/system/zephyr/system.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/config.h" #if defined(ZENOH_ZEPHYR) #include #if KERNEL_VERSION_MAJOR == 2 #include #else #include #endif #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/system/common/system_error.h" #include "zenoh-pico/system/platform.h" /*------------------ Random ------------------*/ uint8_t z_random_u8(void) { return z_random_u32(); } uint16_t z_random_u16(void) { return z_random_u32(); } uint32_t z_random_u32(void) { return sys_rand32_get(); } uint64_t z_random_u64(void) { uint64_t ret = 0; ret |= z_random_u32(); ret = ret << 32; ret |= z_random_u32(); return ret; } void z_random_fill(void *buf, size_t len) { sys_rand_get(buf, len); } /*------------------ Memory ------------------*/ void *z_malloc(size_t size) { return k_malloc(size); } void *z_realloc(void *ptr, size_t size) { // k_realloc not implemented in Zephyr return NULL; } void z_free(void *ptr) { k_free(ptr); } #if Z_FEATURE_MULTI_THREAD == 1 #define Z_THREADS_NUM 4 #ifdef CONFIG_TEST_EXTRA_STACK_SIZE #define Z_PTHREAD_STACK_SIZE_DEFAULT CONFIG_MAIN_STACK_SIZE + CONFIG_TEST_EXTRA_STACK_SIZE #elif CONFIG_TEST_EXTRA_STACKSIZE #define Z_PTHREAD_STACK_SIZE_DEFAULT CONFIG_MAIN_STACK_SIZE + CONFIG_TEST_EXTRA_STACKSIZE #else #define Z_PTHREAD_STACK_SIZE_DEFAULT CONFIG_MAIN_STACK_SIZE #endif K_THREAD_STACK_ARRAY_DEFINE(thread_stack_area, Z_THREADS_NUM, Z_PTHREAD_STACK_SIZE_DEFAULT); static int thread_index = 0; /*------------------ Task ------------------*/ z_result_t _z_task_init(_z_task_t *task, z_task_attr_t *attr, void *(*fun)(void *), void *arg) { z_task_attr_t *lattr = NULL; z_task_attr_t tmp; if (attr == NULL) { (void)pthread_attr_init(&tmp); (void)pthread_attr_setstack(&tmp, &thread_stack_area[thread_index++], Z_PTHREAD_STACK_SIZE_DEFAULT); lattr = &tmp; } _Z_CHECK_SYS_ERR(pthread_create(task, lattr, fun, arg)); } z_result_t _z_task_join(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_join(*task, NULL)); } z_result_t _z_task_detach(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_detach(*task)); } z_result_t _z_task_cancel(_z_task_t *task) { _Z_CHECK_SYS_ERR(pthread_cancel(*task)); } void _z_task_exit(void) { pthread_exit(NULL); } void _z_task_free(_z_task_t **task) { _z_task_t *ptr = *task; z_free(ptr); *task = NULL; } _z_task_id_t _z_task_get_id(const _z_task_t *task) { return *task; } _z_task_id_t _z_task_current_id(void) { return pthread_self(); } bool _z_task_id_equal(const _z_task_id_t *l, const _z_task_id_t *r) { return pthread_equal(*l, *r) != 0; } /*------------------ Mutex ------------------*/ z_result_t _z_mutex_init(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_init(m, NULL)); } z_result_t _z_mutex_drop(_z_mutex_t *m) { if (m == NULL) { return 0; } _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_try_lock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_unlock(_z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } z_result_t _z_mutex_rec_init(_z_mutex_rec_t *m) { pthread_mutexattr_t attr; _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_init(&attr)); _Z_RETURN_IF_SYS_ERR(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)); _Z_RETURN_IF_SYS_ERR(pthread_mutex_init(m, &attr)); _Z_CHECK_SYS_ERR(pthread_mutexattr_destroy(&attr)); } z_result_t _z_mutex_rec_drop(_z_mutex_rec_t *m) { if (m == NULL) { return 0; } _Z_CHECK_SYS_ERR(pthread_mutex_destroy(m)); } z_result_t _z_mutex_rec_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_lock(m)); } z_result_t _z_mutex_rec_try_lock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_trylock(m)); } z_result_t _z_mutex_rec_unlock(_z_mutex_rec_t *m) { _Z_CHECK_SYS_ERR(pthread_mutex_unlock(m)); } /*------------------ Condvar ------------------*/ z_result_t _z_condvar_init(_z_condvar_t *cv) { pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); _Z_CHECK_SYS_ERR(pthread_cond_init(cv, &attr)); } z_result_t _z_condvar_drop(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_destroy(cv)); } z_result_t _z_condvar_signal(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_signal(cv)); } z_result_t _z_condvar_signal_all(_z_condvar_t *cv) { _Z_CHECK_SYS_ERR(pthread_cond_broadcast(cv)); } z_result_t _z_condvar_wait(_z_condvar_t *cv, _z_mutex_t *m) { _Z_CHECK_SYS_ERR(pthread_cond_wait(cv, m)); } z_result_t _z_condvar_wait_until(_z_condvar_t *cv, _z_mutex_t *m, const z_clock_t *abstime) { int error = pthread_cond_timedwait(cv, m, abstime); if (error == ETIMEDOUT) { return Z_ETIMEDOUT; } _Z_CHECK_SYS_ERR(error); } #endif // Z_FEATURE_MULTI_THREAD == 1 /*------------------ Sleep ------------------*/ z_result_t z_sleep_us(size_t time) { int32_t rem = time; while (rem > 0) { rem = k_usleep(rem); // This function is unlikely to work as expected without kernel tuning. // In particular, because the lower bound on the duration of a sleep is the // duration of a tick, CONFIG_SYS_CLOCK_TICKS_PER_SEC must be adjusted to // achieve the resolution desired. The implications of doing this must be // understood before attempting to use k_usleep(). Use with caution. // From: https://docs.zephyrproject.org/apidoc/latest/group__thread__apis.html } return 0; } z_result_t z_sleep_ms(size_t time) { int32_t rem = time; while (rem > 0) { rem = k_msleep(rem); } return 0; } z_result_t z_sleep_s(size_t time) { int32_t rem = time; while (rem > 0) { rem = k_sleep(K_SECONDS(rem)); } return 0; } /*------------------ Instant ------------------*/ z_clock_t z_clock_now(void) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return now; } unsigned long zp_clock_elapsed_us_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_ms_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (1000 * (instant->tv_sec - epoch->tv_sec) + (instant->tv_nsec - epoch->tv_nsec) / 1000000); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long zp_clock_elapsed_s_since(z_clock_t *instant, z_clock_t *epoch) { long elapsed = (instant->tv_sec - epoch->tv_sec); return elapsed > 0 ? (unsigned long)elapsed : 0; } unsigned long z_clock_elapsed_us(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_us_since(&now, instant); } unsigned long z_clock_elapsed_ms(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_ms_since(&now, instant); } unsigned long z_clock_elapsed_s(z_clock_t *instant) { z_clock_t now; clock_gettime(CLOCK_MONOTONIC, &now); return zp_clock_elapsed_s_since(&now, instant); } void z_clock_advance_us(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000000; clock->tv_nsec += (duration % 1000000) * 1000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_ms(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration / 1000; clock->tv_nsec += (duration % 1000) * 1000000; if (clock->tv_nsec >= 1000000000) { clock->tv_sec += 1; clock->tv_nsec -= 1000000000; } } void z_clock_advance_s(z_clock_t *clock, unsigned long duration) { clock->tv_sec += duration; } /*------------------ Time ------------------*/ z_time_t z_time_now(void) { z_time_t now; gettimeofday(&now, NULL); return now; } const char *z_time_now_as_str(char *const buf, unsigned long buflen) { z_time_t tv = z_time_now(); struct tm ts; ts = *localtime(&tv.tv_sec); strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &ts); return buf; } unsigned long z_time_elapsed_us(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec)); return elapsed; } unsigned long z_time_elapsed_ms(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = (1000 * (now.tv_sec - time->tv_sec) + (now.tv_usec - time->tv_usec) / 1000); return elapsed; } unsigned long z_time_elapsed_s(z_time_t *time) { z_time_t now; gettimeofday(&now, NULL); unsigned long elapsed = now.tv_sec - time->tv_sec; return elapsed; } z_result_t _z_get_time_since_epoch(_z_time_since_epoch *t) { z_time_t now; gettimeofday(&now, NULL); t->secs = now.tv_sec; t->nanos = now.tv_usec * 1000; return 0; } #endif /* defined(ZENOH_ZEPHYR) */ ================================================ FILE: src/transport/common/lease.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/lease.h" #include #include "zenoh-pico/transport/multicast/lease.h" #include "zenoh-pico/transport/unicast/lease.h" z_result_t _z_send_keep_alive(_z_transport_t *zt) { z_result_t ret = _Z_RES_OK; switch (zt->_type) { #if Z_FEATURE_UNICAST_TRANSPORT == 1 case _Z_TRANSPORT_UNICAST_TYPE: ret = _zp_unicast_send_keep_alive(&zt->_transport._unicast); break; #endif #if Z_FEATURE_MULTICAST_TRANSPORT == 1 case _Z_TRANSPORT_MULTICAST_TYPE: ret = _zp_multicast_send_keep_alive(&zt->_transport._multicast); break; #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_TRANSPORT_RAWETH_TYPE: ret = _zp_multicast_send_keep_alive(&zt->_transport._raweth); break; #endif default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } z_result_t _z_send_join(_z_transport_t *zt) { z_result_t ret = _Z_RES_OK; // Join task only applies to multicast transports switch (zt->_type) { #if Z_FEATURE_MULTICAST_TRANSPORT == 1 case _Z_TRANSPORT_MULTICAST_TYPE: ret = _zp_multicast_send_join(&zt->_transport._multicast); break; #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_TRANSPORT_RAWETH_TYPE: ret = _zp_multicast_send_join(&zt->_transport._raweth); break; #endif default: _ZP_UNUSED(zt); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } ================================================ FILE: src/transport/common/read.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/read.h" #include #include "zenoh-pico/transport/multicast/read.h" #include "zenoh-pico/transport/raweth/read.h" #include "zenoh-pico/transport/unicast/read.h" z_result_t _z_read(_z_transport_t *zt, bool single_read) { z_result_t ret = _Z_RES_OK; switch (zt->_type) { #if Z_FEATURE_UNICAST_TRANSPORT == 1 case _Z_TRANSPORT_UNICAST_TYPE: ret = _zp_unicast_read(&zt->_transport._unicast, single_read); break; #endif #if Z_FEATURE_MULTICAST_TRANSPORT == 1 case _Z_TRANSPORT_MULTICAST_TYPE: ret = _zp_multicast_read(&zt->_transport._multicast, single_read); break; #endif #if Z_FEATURE_RAWETH_TRANSPORT == 1 case _Z_TRANSPORT_RAWETH_TYPE: ret = _zp_raweth_read(&zt->_transport._raweth, single_read); break; #endif default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } ================================================ FILE: src/transport/common/rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/rx.h" #include #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/logging.h" /*------------------ Reception helper ------------------*/ size_t _z_read_stream_size(_z_zbuf_t *zbuf) { uint8_t stream_size[_Z_MSG_LEN_ENC_SIZE]; // Read the bytes from stream for (uint8_t i = 0; i < _Z_MSG_LEN_ENC_SIZE; i++) { stream_size[i] = _z_zbuf_read(zbuf); } return _z_host_le_load16(stream_size); } z_result_t _z_link_recv_t_msg_cap_flow_stream(const _z_link_t *zl, _z_zbuf_t *zbf, _z_sys_net_socket_t *socket) { // Read the message length size_t read = _z_link_recv_exact_zbuf(zl, zbf, _Z_MSG_LEN_ENC_SIZE, NULL, socket); if (read == _Z_MSG_LEN_ENC_SIZE) { size_t len = 0; for (uint8_t i = 0; i < _Z_MSG_LEN_ENC_SIZE; i++) { len |= (size_t)(_z_zbuf_read(zbf) << (i * (uint8_t)8)); } size_t writable = _z_zbuf_capacity(zbf) - _z_zbuf_len(zbf); if (writable >= len) { // Read enough bytes to decode the message if (_z_link_recv_exact_zbuf(zl, zbf, len, NULL, socket) != len) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_RX_FAILED); return _Z_ERR_TRANSPORT_RX_FAILED; } } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NO_SPACE); return _Z_ERR_TRANSPORT_NO_SPACE; } } else if (read == SIZE_MAX) { return Z_ETIMEDOUT; } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_RX_FAILED); return _Z_ERR_TRANSPORT_RX_FAILED; } return _Z_RES_OK; } z_result_t _z_link_recv_t_msg_cap_flow_datagram(const _z_link_t *zl, _z_zbuf_t *zbf, _z_sys_net_socket_t *socket) { _ZP_UNUSED(socket); if (_z_link_recv_zbuf(zl, zbf, NULL) == SIZE_MAX) { return Z_ETIMEDOUT; } else { return _Z_RES_OK; } } z_result_t _z_link_recv_t_msg(_z_transport_message_t *t_msg, const _z_link_t *zl, _z_sys_net_socket_t *socket, z_clock_t recv_deadline) { // Create and prepare the buffer _z_zbuf_t zbf = _z_zbuf_make(Z_BATCH_UNICAST_SIZE); _z_zbuf_reset(&zbf); z_result_t ret = Z_ETIMEDOUT; while (ret == Z_ETIMEDOUT) { switch (zl->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: ret = _z_link_recv_t_msg_cap_flow_stream(zl, &zbf, socket); break; case Z_LINK_CAP_FLOW_DATAGRAM: ret = _z_link_recv_t_msg_cap_flow_datagram(zl, &zbf, socket); break; default: _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } if (ret == Z_ETIMEDOUT) { z_clock_t now = z_clock_now(); if (zp_clock_elapsed_ms_since(&recv_deadline, &now) == 0) { ret = _Z_ERR_TRANSPORT_RX_DURATION_EXPIRED; } } } if (ret == _Z_RES_OK) { _z_transport_message_t l_t_msg; ret = _z_transport_message_decode(&l_t_msg, &zbf); if (ret == _Z_RES_OK) { _z_t_msg_copy(t_msg, &l_t_msg); } } else { _Z_ERROR_LOG(ret); } _z_zbuf_clear(&zbf); return ret; } ================================================ FILE: src/transport/common/transport.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/transport/transport.h" #include #include "zenoh-pico/link/link.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/unicast/accept.h" #include "zenoh-pico/utils/result.h" void _z_transport_common_clear(_z_transport_common_t *ztc) { #if Z_FEATURE_MULTI_THREAD == 1 // Clean up the mutexes _z_mutex_drop(&ztc->_mutex_tx); _z_mutex_rec_drop(&ztc->_mutex_peer); #endif // Clean up the buffers _z_wbuf_clear(&ztc->_wbuf); _z_zbuf_clear(&ztc->_zbuf); _z_link_free(&ztc->_link); _z_session_weak_drop(&ztc->_session); } ================================================ FILE: src/transport/common/tx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/api/constants.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/transport/raweth/tx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/endianness.h" #include "zenoh-pico/utils/logging.h" #if defined(Z_TEST_HOOKS) #include "zenoh-pico/session/loopback.h" static _z_session_send_override_fn _z_send_n_msg_override = NULL; void _z_transport_set_send_n_msg_override(_z_session_send_override_fn fn) { _z_send_n_msg_override = fn; } #endif /*------------------ Transmission helper ------------------*/ static inline bool _z_transport_tx_get_express_status(const _z_network_message_t *msg) { switch (msg->_tag) { case _Z_N_DECLARE: return _Z_HAS_FLAG(msg->_body._declare._ext_qos._val, _Z_N_QOS_IS_EXPRESS_FLAG); case _Z_N_PUSH: return _Z_HAS_FLAG(msg->_body._push._qos._val, _Z_N_QOS_IS_EXPRESS_FLAG); case _Z_N_REQUEST: return _Z_HAS_FLAG(msg->_body._request._ext_qos._val, _Z_N_QOS_IS_EXPRESS_FLAG); case _Z_N_RESPONSE: return _Z_HAS_FLAG(msg->_body._response._ext_qos._val, _Z_N_QOS_IS_EXPRESS_FLAG); default: return false; } } static _z_zint_t _z_transport_tx_get_sn(_z_transport_common_t *ztc, z_reliability_t reliability) { _z_zint_t sn; if (reliability == Z_RELIABILITY_RELIABLE) { sn = ztc->_sn_tx_reliable; ztc->_sn_tx_reliable = _z_sn_increment(ztc->_sn_res, ztc->_sn_tx_reliable); } else { sn = ztc->_sn_tx_best_effort; ztc->_sn_tx_best_effort = _z_sn_increment(ztc->_sn_res, ztc->_sn_tx_best_effort); } return sn; } #if Z_FEATURE_FRAGMENTATION == 1 static z_result_t _z_transport_tx_send_fragment_inner(_z_transport_common_t *ztc, _z_wbuf_t *frag_buff, const _z_network_message_t *n_msg, z_reliability_t reliability, _z_zint_t first_sn, _z_transport_peer_unicast_slist_t *peers) { bool is_first = true; _z_zint_t sn = first_sn; // Encode message on temp buffer _Z_RETURN_IF_ERR(_z_network_message_encode(frag_buff, n_msg)); // Fragment message while (_z_wbuf_len(frag_buff) > 0) { // Get fragment sequence number if (!is_first) { sn = _z_transport_tx_get_sn(ztc, reliability); } // Serialize fragment __unsafe_z_prepare_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); z_result_t ret = __unsafe_z_serialize_zenoh_fragment(&ztc->_wbuf, frag_buff, reliability, sn, is_first); if (ret != _Z_RES_OK) { _Z_ERROR("Fragment serialization failed with err %d", ret); return ret; } // Send fragment __unsafe_z_finalize_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); if (peers == NULL) { _Z_RETURN_IF_ERR(_z_link_send_wbuf(ztc->_link, &ztc->_wbuf, NULL)); } else { _z_transport_peer_unicast_slist_t *curr_list = peers; while (curr_list != NULL) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(curr_list); // Send on peer socket _z_link_send_wbuf(ztc->_link, &ztc->_wbuf, &curr_peer->_socket); curr_list = _z_transport_peer_unicast_slist_next(curr_list); } } ztc->_transmitted = true; // Tell session we transmitted data is_first = false; } return _Z_RES_OK; } static z_result_t _z_transport_tx_send_fragment(_z_transport_common_t *ztc, const _z_network_message_t *n_msg, z_reliability_t reliability, _z_zint_t first_sn, _z_transport_peer_unicast_slist_t *peers) { // Create an expandable wbuf for fragmentation _z_wbuf_t frag_buff = _z_wbuf_make(_Z_FRAG_BUFF_BASE_SIZE, true); // Send message as fragments z_result_t ret = _z_transport_tx_send_fragment_inner(ztc, &frag_buff, n_msg, reliability, first_sn, peers); // Clear the buffer as it's no longer required _z_wbuf_clear(&frag_buff); return ret; } #else static z_result_t _z_transport_tx_send_fragment(_z_transport_common_t *ztc, const _z_network_message_t *n_msg, z_reliability_t reliability, _z_zint_t first_sn, _z_transport_peer_unicast_slist_t *peers) { _ZP_UNUSED(ztc); _ZP_UNUSED(n_msg); _ZP_UNUSED(reliability); _ZP_UNUSED(first_sn); _ZP_UNUSED(peers); _Z_INFO("Sending the message required fragmentation feature that is deactivated."); return _Z_RES_OK; } #endif static inline bool _z_transport_tx_batch_has_data(_z_transport_common_t *ztc) { #if Z_FEATURE_BATCHING == 1 return (ztc->_batch_state == _Z_BATCHING_ACTIVE) && (ztc->_batch_count > 0); #else _ZP_UNUSED(ztc); return false; #endif } static z_result_t _z_transport_tx_flush_buffer(_z_transport_common_t *ztc, _z_transport_peer_unicast_slist_t *peers) { __unsafe_z_finalize_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); // Send network message if (peers == NULL) { _Z_RETURN_IF_ERR(_z_link_send_wbuf(ztc->_link, &ztc->_wbuf, NULL)); } else { _z_transport_peer_unicast_slist_t *curr_list = peers; while (curr_list != NULL) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(curr_list); // Send on peer socket _z_link_send_wbuf(ztc->_link, &ztc->_wbuf, &curr_peer->_socket); curr_list = _z_transport_peer_unicast_slist_next(curr_list); } } ztc->_transmitted = true; // Tell session we transmitted data #if Z_FEATURE_BATCHING == 1 ztc->_batch_count = 0; #endif return _Z_RES_OK; } static z_result_t _z_transport_tx_flush_or_incr_batch(_z_transport_common_t *ztc, _z_transport_peer_unicast_slist_t *peers) { #if Z_FEATURE_BATCHING == 1 if (ztc->_batch_state == _Z_BATCHING_ACTIVE) { // Increment batch count ztc->_batch_count++; return _Z_RES_OK; } else { return _z_transport_tx_flush_buffer(ztc, peers); } #else return _z_transport_tx_flush_buffer(ztc, peers); #endif } static z_result_t _z_transport_tx_batch_overflow(_z_transport_common_t *ztc, const _z_network_message_t *n_msg, z_reliability_t reliability, _z_zint_t sn, size_t prev_wpos, _z_transport_peer_unicast_slist_t *peers) { #if Z_FEATURE_BATCHING == 1 // Remove partially encoded data _z_wbuf_set_wpos(&ztc->_wbuf, prev_wpos); // Send batch _Z_RETURN_IF_ERR(_z_transport_tx_flush_buffer(ztc, peers)); // Init buffer __unsafe_z_prepare_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); sn = _z_transport_tx_get_sn(ztc, reliability); _z_transport_message_t t_msg = _z_t_msg_make_frame_header(sn, reliability); _Z_RETURN_IF_ERR(_z_transport_message_encode(&ztc->_wbuf, &t_msg)); // Retry encode z_result_t ret = _z_network_message_encode(&ztc->_wbuf, n_msg); if (ret != _Z_RES_OK) { // Message still doesn't fit in buffer, send as fragments return _z_transport_tx_send_fragment(ztc, n_msg, reliability, sn, peers); } else { if (_z_transport_tx_get_express_status(n_msg)) { // Send immediately return _z_transport_tx_flush_buffer(ztc, peers); } else { // Increment batch ztc->_batch_count++; } } return _Z_RES_OK; #else _ZP_UNUSED(ztc); _ZP_UNUSED(n_msg); _ZP_UNUSED(reliability); _ZP_UNUSED(sn); _ZP_UNUSED(prev_wpos); _ZP_UNUSED(peers); return _Z_RES_OK; #endif } static inline size_t _z_transport_tx_save_wpos(_z_wbuf_t *wbuf) { #if Z_FEATURE_BATCHING == 1 return _z_wbuf_get_wpos(wbuf); #else _ZP_UNUSED(wbuf); return 0; #endif } static z_result_t _z_transport_tx_send_n_msg_inner(_z_transport_common_t *ztc, const _z_network_message_t *n_msg, z_reliability_t reliability, _z_transport_peer_unicast_slist_t *peers) { // Init buffer _z_zint_t sn = 0; bool batch_has_data = _z_transport_tx_batch_has_data(ztc); if (!batch_has_data) { __unsafe_z_prepare_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); sn = _z_transport_tx_get_sn(ztc, reliability); _z_transport_message_t t_msg = _z_t_msg_make_frame_header(sn, reliability); _Z_RETURN_IF_ERR(_z_transport_message_encode(&ztc->_wbuf, &t_msg)); } // Try encoding the network message size_t prev_wpos = _z_transport_tx_save_wpos(&ztc->_wbuf); z_result_t ret = _z_network_message_encode(&ztc->_wbuf, n_msg); if (ret == _Z_RES_OK) { if (_z_transport_tx_get_express_status(n_msg)) { // Send immediately return _z_transport_tx_flush_buffer(ztc, peers); } else { // Flush buffer or increase batch return _z_transport_tx_flush_or_incr_batch(ztc, peers); } } else if (!batch_has_data) { // Message doesn't fit in buffer, send as fragments return _z_transport_tx_send_fragment(ztc, n_msg, reliability, sn, peers); } else { // Buffer is too full for message return _z_transport_tx_batch_overflow(ztc, n_msg, reliability, sn, prev_wpos, peers); } } static z_result_t _z_transport_tx_send_t_msg_inner(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg, _z_transport_peer_unicast_slist_t *peers) { // Send batch if needed bool batch_has_data = _z_transport_tx_batch_has_data(ztc); if (batch_has_data) { _Z_RETURN_IF_ERR(_z_transport_tx_flush_buffer(ztc, peers)); } // Encode transport message __unsafe_z_prepare_wbuf(&ztc->_wbuf, ztc->_link->_cap._flow); _Z_RETURN_IF_ERR(_z_transport_message_encode(&ztc->_wbuf, t_msg)); // Send message return _z_transport_tx_flush_buffer(ztc, peers); } z_result_t _z_transport_tx_send_t_msg(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg, _z_transport_peer_unicast_slist_t *peers) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Send session message"); // If sending to a peer list, make sure the peer mutex is locked _z_transport_tx_mutex_lock(ztc, true); ret = _z_transport_tx_send_t_msg_inner(ztc, t_msg, peers); _z_transport_tx_mutex_unlock(ztc); return ret; } z_result_t _z_transport_tx_send_t_msg_wrapper(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg) { return _z_transport_tx_send_t_msg(ztc, t_msg, NULL); } static z_result_t _z_transport_tx_send_n_msg(_z_transport_common_t *ztc, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl, _z_transport_peer_unicast_slist_t *peers) { z_result_t ret = _Z_RES_OK; _Z_DEBUG("Send network message"); // Acquire the lock and drop the message if needed if (!_z_transport_batch_hold_tx_mutex()) { ret = _z_transport_tx_mutex_lock(ztc, cong_ctrl == Z_CONGESTION_CONTROL_BLOCK); } if (ret != _Z_RES_OK) { _Z_INFO("Dropping zenoh message because of congestion control"); return ret; } // Process message ret = _z_transport_tx_send_n_msg_inner(ztc, n_msg, reliability, peers); if (!_z_transport_batch_hold_tx_mutex()) { _z_transport_tx_mutex_unlock(ztc); } return ret; } static z_result_t _z_transport_tx_send_n_batch(_z_transport_common_t *ztc, z_congestion_control_t cong_ctrl, _z_transport_peer_unicast_slist_t *peers) { #if Z_FEATURE_BATCHING == 1 z_result_t ret = _Z_RES_OK; // Check batch size if (ztc->_batch_count > 0) { // Acquire the lock and drop the message if needed if (!_z_transport_batch_hold_tx_mutex()) { ret = _z_transport_tx_mutex_lock(ztc, cong_ctrl == Z_CONGESTION_CONTROL_BLOCK); } if (ret != _Z_RES_OK) { _Z_INFO("Dropping zenoh batch because of congestion control"); return ret; } // Send batch _Z_DEBUG("Send network batch"); ret = _z_transport_tx_flush_buffer(ztc, peers); if (!_z_transport_batch_hold_tx_mutex()) { _z_transport_tx_mutex_unlock(ztc); } return ret; } return _Z_RES_OK; #else _ZP_UNUSED(ztc); _ZP_UNUSED(cong_ctrl); _ZP_UNUSED(peers); return _Z_RES_OK; #endif } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - ztu->mutex_tx */ void __unsafe_z_prepare_wbuf(_z_wbuf_t *buf, uint8_t link_flow_capability) { _z_wbuf_reset(buf); switch (link_flow_capability) { // Stream capable links case Z_LINK_CAP_FLOW_STREAM: for (uint8_t i = 0; i < _Z_MSG_LEN_ENC_SIZE; i++) { _z_wbuf_put(buf, 0, i); } _z_wbuf_set_wpos(buf, _Z_MSG_LEN_ENC_SIZE); break; // Datagram capable links case Z_LINK_CAP_FLOW_DATAGRAM: default: break; } } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - ztu->mutex_tx */ void __unsafe_z_finalize_wbuf(_z_wbuf_t *buf, uint8_t link_flow_capability) { switch (link_flow_capability) { // Stream capable links case Z_LINK_CAP_FLOW_STREAM: { size_t len = _z_wbuf_len(buf) - _Z_MSG_LEN_ENC_SIZE; // Encode the u16 size as little endian _z_wbuf_put(buf, _z_get_u16_lsb((uint_fast16_t)len), 0); _z_wbuf_put(buf, _z_get_u16_msb((uint_fast16_t)len), 1); break; } // Datagram capable links case Z_LINK_CAP_FLOW_DATAGRAM: default: break; } } z_result_t _z_send_t_msg(_z_transport_t *zt, const _z_transport_message_t *t_msg) { z_result_t ret = _Z_RES_OK; switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: ret = _z_transport_tx_send_t_msg(&zt->_transport._unicast._common, t_msg, NULL); break; case _Z_TRANSPORT_MULTICAST_TYPE: ret = _z_transport_tx_send_t_msg(&zt->_transport._multicast._common, t_msg, NULL); break; case _Z_TRANSPORT_RAWETH_TYPE: ret = _z_raweth_send_t_msg(&zt->_transport._raweth._common, t_msg); break; default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } z_result_t _z_link_send_t_msg(const _z_link_t *zl, const _z_transport_message_t *t_msg, _z_sys_net_socket_t *socket) { z_result_t ret = _Z_RES_OK; // Create and prepare the buffer to serialize the message on uint16_t mtu = (zl->_mtu < Z_BATCH_UNICAST_SIZE) ? zl->_mtu : Z_BATCH_UNICAST_SIZE; _z_wbuf_t wbf = _z_wbuf_make(mtu, false); if (_z_wbuf_capacity(&wbf) != mtu) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } switch (zl->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: for (uint8_t i = 0; i < _Z_MSG_LEN_ENC_SIZE; i++) { _z_wbuf_put(&wbf, 0, i); } _z_wbuf_set_wpos(&wbf, _Z_MSG_LEN_ENC_SIZE); break; case Z_LINK_CAP_FLOW_DATAGRAM: break; default: _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } // Encode the session message ret = _z_transport_message_encode(&wbf, t_msg); if (ret == _Z_RES_OK) { switch (zl->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: { // Write the message length in the reserved space if needed size_t len = _z_wbuf_len(&wbf) - _Z_MSG_LEN_ENC_SIZE; for (uint8_t i = 0; i < _Z_MSG_LEN_ENC_SIZE; i++) { _z_wbuf_put(&wbf, (uint8_t)((len >> (uint8_t)8 * i) & (uint8_t)0xFF), i); } break; } case Z_LINK_CAP_FLOW_DATAGRAM: break; default: _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } // Send the wbuf on the socket ret = _z_link_send_wbuf(zl, &wbf, socket); } _z_wbuf_clear(&wbf); return ret; } z_result_t __unsafe_z_serialize_zenoh_fragment(_z_wbuf_t *dst, _z_wbuf_t *src, z_reliability_t reliability, size_t sn, bool first) { z_result_t ret = _Z_RES_OK; // Assume first that this is not the final fragment bool is_final = false; do { size_t w_pos = _z_wbuf_get_wpos(dst); // Mark the buffer for the writing operation _z_transport_message_t f_hdr = _z_t_msg_make_fragment_header(sn, reliability == Z_RELIABILITY_RELIABLE, is_final, first, false); ret = _z_transport_message_encode(dst, &f_hdr); // Encode the frame header if (ret == _Z_RES_OK) { size_t space_left = _z_wbuf_space_left(dst); size_t bytes_left = _z_wbuf_len(src); if ((is_final == false) && (bytes_left <= space_left)) { // Check if it is really the final fragment _z_wbuf_set_wpos(dst, w_pos); // Revert the buffer is_final = true; // It is really the finally fragment, reserialize the header continue; } size_t to_copy = (bytes_left <= space_left) ? bytes_left : space_left; // Compute bytes to write ret = _z_wbuf_siphon(dst, src, to_copy); // Write the fragment } break; } while (1); return ret; } z_result_t _z_send_n_msg(_z_session_t *zn, const _z_network_message_t *z_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl, void *peer) { #if defined(Z_TEST_HOOKS) if (_z_send_n_msg_override != NULL) { bool handled = false; z_result_t override_ret = _z_send_n_msg_override(zn, z_msg, reliability, cong_ctrl, peer, &handled); if (handled) { return override_ret; } } #endif z_result_t ret = _Z_RES_OK; // Call transport function switch (zn->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_transport_common_t *ztc = &zn->_tp._transport._unicast._common; if (zn->_mode == Z_WHATAMI_CLIENT) { ret = _z_transport_tx_send_n_msg(ztc, z_msg, reliability, cong_ctrl, NULL); } else if (!_z_transport_peer_unicast_slist_is_empty(zn->_tp._transport._unicast._peers)) { if (!_z_transport_batch_hold_peer_mutex()) { _z_transport_peer_mutex_lock(ztc); } if (peer == NULL) { ret = _z_transport_tx_send_n_msg(ztc, z_msg, reliability, cong_ctrl, zn->_tp._transport._unicast._peers); } else { // Send to a single peer, convert to peer list _z_transport_peer_unicast_slist_t *dst_list = _z_transport_peer_unicast_slist_push_empty(NULL); if (dst_list != NULL) { memcpy(_z_transport_peer_unicast_slist_value(dst_list), (_z_transport_peer_unicast_t *)peer, sizeof(_z_transport_peer_unicast_t)); // Send message ret = _z_transport_tx_send_n_msg(ztc, z_msg, reliability, cong_ctrl, dst_list); z_free(dst_list); } } if (!_z_transport_batch_hold_peer_mutex()) { _z_transport_peer_mutex_unlock(ztc); } } } break; case _Z_TRANSPORT_MULTICAST_TYPE: ret = _z_transport_tx_send_n_msg(&zn->_tp._transport._multicast._common, z_msg, reliability, cong_ctrl, NULL); break; case _Z_TRANSPORT_RAWETH_TYPE: ret = _z_raweth_send_n_msg(zn, z_msg, reliability, cong_ctrl); break; default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } z_result_t _z_send_n_batch(_z_session_t *zn, z_congestion_control_t cong_ctrl) { z_result_t ret = _Z_RES_OK; // Call transport function switch (zn->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: if (zn->_mode == Z_WHATAMI_CLIENT) { ret = _z_transport_tx_send_n_batch(&zn->_tp._transport._unicast._common, cong_ctrl, NULL); } else if (!_z_transport_peer_unicast_slist_is_empty(zn->_tp._transport._unicast._peers)) { _z_transport_peer_mutex_lock(&zn->_tp._transport._unicast._common); ret = _z_transport_tx_send_n_batch(&zn->_tp._transport._unicast._common, cong_ctrl, zn->_tp._transport._unicast._peers); _z_transport_peer_mutex_unlock(&zn->_tp._transport._unicast._common); } break; case _Z_TRANSPORT_MULTICAST_TYPE: ret = _z_transport_tx_send_n_batch(&zn->_tp._transport._multicast._common, cong_ctrl, NULL); break; case _Z_TRANSPORT_RAWETH_TYPE: _Z_INFO("Batching not yet supported on raweth transport"); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_TX_FAILED); ret = _Z_ERR_TRANSPORT_TX_FAILED; break; default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } ================================================ FILE: src/transport/manager.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/manager.h" #include #include #include "zenoh-pico/link/transport/socket.h" #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/transport/tls_stream.h" #endif #include "zenoh-pico/link/link.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/unicast/accept.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/utils/sleep.h" #if Z_FEATURE_CONNECTIVITY == 1 static void _z_new_peer_dispatch_connected_event(_z_transport_unicast_t *ztu, const _z_transport_peer_unicast_t *peer) { if (ztu == NULL || peer == NULL) { return; } _z_connectivity_peer_event_data_t connected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; bool has_event_data = false; _z_transport_peer_mutex_lock(&ztu->_common); _z_transport_peer_unicast_slist_t *it = ztu->_peers; while (it != NULL) { _z_transport_peer_unicast_t *current_peer = _z_transport_peer_unicast_slist_value(it); if (current_peer == peer) { _z_transport_get_link_properties(&ztu->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&connected_peer, ¤t_peer->common); has_event_data = true; break; } it = _z_transport_peer_unicast_slist_next(it); } _z_transport_peer_mutex_unlock(&ztu->_common); if (has_event_data) { _z_connectivity_peer_connected(_z_transport_common_get_session(&ztu->_common), &connected_peer, false, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&connected_peer); } } #endif static z_result_t _z_new_transport_client(_z_transport_t *zt, const _z_string_t *locator, const _z_id_t *local_zid, const _z_config_t *session_cfg) { z_result_t ret = _Z_RES_OK; // Init link _z_link_t *zl = (_z_link_t *)z_malloc(sizeof(_z_link_t)); if (zl == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } memset(zl, 0, sizeof(_z_link_t)); // Open link ret = _z_open_link(zl, locator, session_cfg); if (ret != _Z_RES_OK) { z_free(zl); return ret; } // Open transport switch (zl->_cap._transport) { // Unicast transport case Z_LINK_CAP_TRANSPORT_UNICAST: { _z_transport_unicast_establish_param_t tp_param; ret = _z_unicast_open_client(&tp_param, zl, local_zid); if (ret != _Z_RES_OK) { _z_link_free(&zl); return ret; } ret = _z_unicast_transport_create(zt, zl, &tp_param); // Fill peer list if (ret == _Z_RES_OK) { ret = _z_transport_peer_unicast_add(&zt->_transport._unicast, &tp_param, *_z_link_get_socket(zl), false, NULL); } break; } // Multicast transport case Z_LINK_CAP_TRANSPORT_RAWETH: case Z_LINK_CAP_TRANSPORT_MULTICAST: { _z_transport_multicast_establish_param_t tp_param = {0}; ret = _z_multicast_open_client(&tp_param, zl, local_zid); if (ret != _Z_RES_OK) { _z_link_free(&zl); return ret; } ret = _z_multicast_transport_create(zt, zl, &tp_param); break; } default: _Z_ERROR_LOG(_Z_ERR_GENERIC); _z_link_free(&zl); ret = _Z_ERR_GENERIC; break; } return ret; } static z_result_t _z_new_transport_peer(_z_transport_t *zt, const _z_string_t *locator, const _z_id_t *local_zid, int peer_op, const _z_config_t *session_cfg, _z_runtime_t *runtime) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_LINK_TCP != 1 && Z_FEATURE_LINK_TLS != 1 _ZP_UNUSED(runtime); #endif // Init link _z_link_t *zl = (_z_link_t *)z_malloc(sizeof(_z_link_t)); if (zl == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } memset(zl, 0, sizeof(_z_link_t)); // Listen link if (peer_op == _Z_PEER_OP_OPEN) { ret = _z_open_link(zl, locator, session_cfg); } else { ret = _z_listen_link(zl, locator, session_cfg); } if (ret != _Z_RES_OK) { z_free(zl); return ret; } switch (zl->_cap._transport) { case Z_LINK_CAP_TRANSPORT_UNICAST: { #if Z_FEATURE_UNICAST_PEER == 1 _z_transport_unicast_establish_param_t tp_param = {0}; ret = _z_unicast_open_peer(&tp_param, zl, local_zid, peer_op, NULL); if (ret != _Z_RES_OK) { _z_link_free(&zl); return ret; } ret = _z_unicast_transport_create(zt, zl, &tp_param); _Z_SET_IF_OK(ret, _z_socket_set_blocking(_z_link_get_socket(zl), false)); if (ret == _Z_RES_OK) { if (peer_op == _Z_PEER_OP_OPEN) { ret = _z_transport_peer_unicast_add(&zt->_transport._unicast, &tp_param, *_z_link_get_socket(zl), false, NULL); } else { #if Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1 _z_fut_t f = _z_fut_null(); f._fut_arg = &zt->_transport._unicast; f._fut_fn = _zp_unicast_accept_task_fn; if (_z_fut_handle_is_null(_z_runtime_spawn(runtime, &f))) { _Z_ERROR("Failed to spawn unicast accept task after transport creation."); ret = _Z_ERR_FAILED_TO_SPAWN_TASK; } #else _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_FAILED); ret = _Z_ERR_TRANSPORT_OPEN_FAILED; #endif } } #else _ZP_UNUSED(runtime); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_FAILED); ret = _Z_ERR_TRANSPORT_OPEN_FAILED; #endif break; } case Z_LINK_CAP_TRANSPORT_RAWETH: case Z_LINK_CAP_TRANSPORT_MULTICAST: { _z_transport_multicast_establish_param_t tp_param; ret = _z_multicast_open_peer(&tp_param, zl, local_zid); if (ret != _Z_RES_OK) { _z_link_free(&zl); return ret; } ret = _z_multicast_transport_create(zt, zl, &tp_param); break; } default: _Z_ERROR_LOG(_Z_ERR_GENERIC); _z_link_free(&zl); ret = _Z_ERR_GENERIC; break; } return ret; } z_result_t _z_new_transport(_z_transport_t *zt, const _z_id_t *bs, const _z_string_t *locator, z_whatami_t mode, int peer_op, const _z_config_t *session_cfg, _z_runtime_t *runtime) { z_result_t ret; if (mode == Z_WHATAMI_CLIENT) { ret = _z_new_transport_client(zt, locator, bs, session_cfg); } else { ret = _z_new_transport_peer(zt, locator, bs, peer_op, session_cfg, runtime); } return ret; } z_result_t _z_new_peer(_z_transport_t *zt, const _z_id_t *session_id, const _z_string_t *locator, const _z_config_t *session_cfg) { z_result_t ret = _Z_RES_OK; switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: { _z_sys_net_socket_t socket = {0}; ret = _z_open_socket(locator, session_cfg, &socket); if (ret == _Z_ERR_GENERIC) { ret = _Z_ERR_TRANSPORT_OPEN_FAILED; } _Z_RETURN_IF_ERR(ret); _z_transport_unicast_establish_param_t tp_param = {0}; ret = _z_unicast_open_peer(&tp_param, zt->_transport._unicast._common._link, session_id, _Z_PEER_OP_OPEN, &socket); if (ret != _Z_RES_OK) { #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&socket); #endif _z_socket_close(&socket); return ret; } ret = _z_socket_set_blocking(&socket, false); if (ret != _Z_RES_OK) { #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&socket); #endif _z_socket_close(&socket); return ret; } _z_transport_peer_unicast_t *peer = NULL; ret = _z_transport_peer_unicast_add(&zt->_transport._unicast, &tp_param, socket, true, &peer); if ((ret == _Z_RES_OK) && (peer != NULL)) { (void)_z_interest_push_declarations_to_peer( _z_transport_common_get_session(&zt->_transport._unicast._common), &peer->common); } #if Z_FEATURE_CONNECTIVITY == 1 if ((ret == _Z_RES_OK) && (peer != NULL)) { _z_new_peer_dispatch_connected_event(&zt->_transport._unicast, peer); } #endif } break; default: break; } return ret; } bool _z_transport_open_error_is_retryable(z_result_t ret) { switch (ret) { case _Z_ERR_TRANSPORT_OPEN_FAILED: case _Z_ERR_TRANSPORT_TX_FAILED: case _Z_ERR_TRANSPORT_RX_FAILED: case _Z_ERR_TRANSPORT_RX_DURATION_EXPIRED: return true; default: return false; } } #if Z_FEATURE_UNICAST_PEER == 1 /* * Attempt to add all peers currently marked as pending. * * Behaviour: * - Each locator is attempted once per call. * - On success, the locator is marked done. * - On non-retryable error, the locator is marked failed. * - On retryable error, the locator remains pending for future attempts. * * Return values: * - _Z_RES_OK: * All pending peers were added, or there were no pending peers. * * - _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY: * At least one peer was successfully added during this call, but * retryable peers remain. * * - _Z_ERR_TRANSPORT_OPEN_FAILED: * No peer was successfully added during this call, and retryable * peers remain. * * - Any other error: * A non-retryable error occurred; the returned value is the last * such error encountered. */ static z_result_t _z_add_peers_impl(_z_transport_t *zt, const _z_id_t *session_id, _z_pending_peers_t *pending_peers, const _z_config_t *config) { if (!_z_pending_peers_has_pending(pending_peers)) { return _Z_RES_OK; } z_result_t last_non_retryable_ret = _Z_RES_OK; bool peer_added = false; size_t len = _z_pending_peer_svec_len(&pending_peers->_peers); for (size_t i = 0; i < len; i++) { _z_pending_peer_t *peer = _z_pending_peer_svec_get(&pending_peers->_peers, i); if (peer->_state != _Z_PENDING_PEER_STATE_PENDING) { continue; } _z_string_t *locator = &peer->_locator; z_result_t peer_ret = _z_new_peer(zt, session_id, locator, config); if (peer_ret == _Z_RES_OK) { peer->_state = _Z_PENDING_PEER_STATE_DONE; peer_added = true; _Z_DEBUG("Successfully added peer locator [%zu]: %.*s", i, (int)_z_string_len(locator), _z_string_data(locator)); continue; } _Z_DEBUG("Could not add peer locator [%zu]: %.*s (%d)", i, (int)_z_string_len(locator), _z_string_data(locator), peer_ret); if (!_z_transport_open_error_is_retryable(peer_ret)) { peer->_state = _Z_PENDING_PEER_STATE_FAILED; last_non_retryable_ret = peer_ret; _Z_WARN("Peer locator [%zu] (%.*s) removed from pending set due to non-retryable error", i, (int)_z_string_len(locator), _z_string_data(locator)); } } if (last_non_retryable_ret != _Z_RES_OK) { return last_non_retryable_ret; } if (_z_pending_peers_has_pending(pending_peers)) { return peer_added ? _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY : _Z_ERR_TRANSPORT_OPEN_FAILED; } return _Z_RES_OK; } static _z_fut_fn_result_t _z_add_peers_task_ready(_z_pending_peers_t *pending_peers) { _z_pending_peers_clear(pending_peers); return _z_fut_fn_result_ready(); } /* * Add peers for a transport according to the configured failure policy. * * This function operates only on unicast transports. For other transport * types it is a no-op and returns _Z_RES_OK. * * Behaviour: * - Performs at least one attempt to add pending peers. * - If all peers are added, returns _Z_RES_OK. * * Non-retryable errors: * - If exit_on_failure is true, returns immediately with the error. * - Otherwise, tolerates the error and continues if retryable peers remain. * * Retryable peers remaining: * - If timeout_ms == 0: * - exit_on_failure == true: * returns: * _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY if any peer was added * _Z_ERR_TRANSPORT_OPEN_FAILED otherwise * - exit_on_failure == false: * returns _Z_RES_OK * * - If timeout_ms != 0: * - exit_on_failure == true: * retries synchronously with backoff until: * * all peers are added, or * * timeout expires, or * * a non-retryable error occurs * * - exit_on_failure == false: * returns _Z_RES_OK and leaves pending_peers populated for * background retry handling. * * Timeout result (exit_on_failure == true): * - If at least one peer was added before timeout: * returns _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY * - Otherwise: * returns _Z_ERR_TRANSPORT_OPEN_FAILED */ z_result_t _z_add_peers(_z_transport_t *zt, const _z_id_t *session_id, _z_pending_peers_t *pending_peers, const _z_config_t *session_cfg, bool exit_on_failure) { if (zt->_type != _Z_TRANSPORT_UNICAST_TYPE) { _z_pending_peers_clear(pending_peers); return _Z_RES_OK; } bool peer_added = false; while (true) { z_result_t ret = _z_add_peers_impl(zt, session_id, pending_peers, session_cfg); if (ret == _Z_RES_OK) { assert(!_z_pending_peers_has_pending(pending_peers)); _z_pending_peers_clear(pending_peers); return _Z_RES_OK; } if (ret == _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY) { peer_added = true; } else if (ret != _Z_ERR_TRANSPORT_OPEN_FAILED) { // Non-retryable error if (exit_on_failure) { _z_pending_peers_clear(pending_peers); return ret; } if (!_z_pending_peers_has_pending(pending_peers)) { _z_pending_peers_clear(pending_peers); return _Z_RES_OK; } } // Retryable peers remain if (pending_peers->_timeout_ms == 0) { if (!exit_on_failure) { _z_pending_peers_clear(pending_peers); return _Z_RES_OK; } _z_pending_peers_clear(pending_peers); return peer_added ? _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY : _Z_ERR_TRANSPORT_OPEN_FAILED; } if (!exit_on_failure) { _z_pending_peers_move(&zt->_transport._unicast._pending_peers, pending_peers); return _Z_RES_OK; // Let the background task handle retries until success or timeout } if (!_z_backoff_sleep(&pending_peers->_start, pending_peers->_timeout_ms, &pending_peers->_sleep_ms)) { _z_pending_peers_clear(pending_peers); return peer_added ? _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY : _Z_ERR_TRANSPORT_OPEN_FAILED; } } } /* * Continue peer addition after z_open() accepted partial connectivity. * * The task owns ztu->_pending_peers while pending locators remain. It clears * that state when the transport/session can no longer accept peers, when all * pending peers have been added, or when the configured timeout expires. During * transport reconnection it suspends so reconnection can re-establish the base * transport before peer addition resumes. */ _z_fut_fn_result_t _zp_add_peers_task_fn(void *ztu_arg, _z_executor_t *executor) { _ZP_UNUSED(executor); _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)ztu_arg; _z_pending_peers_t *pending_peers = &ztu->_pending_peers; if (ztu->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_add_peers_task_ready(pending_peers); } else if (ztu->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } if (!_z_pending_peers_has_pending(pending_peers)) { return _z_add_peers_task_ready(pending_peers); } _z_session_rc_t session_rc = _z_session_weak_upgrade_if_open(&ztu->_common._session); if (_Z_RC_IS_NULL(&session_rc)) { return _z_add_peers_task_ready(pending_peers); } (void)_z_add_peers_impl(&_Z_RC_IN_VAL(&session_rc)->_tp, &_Z_RC_IN_VAL(&session_rc)->_local_zid, pending_peers, &_Z_RC_IN_VAL(&session_rc)->_config); _z_session_rc_drop(&session_rc); if (!_z_pending_peers_has_pending(pending_peers)) { return _z_add_peers_task_ready(pending_peers); } // Check timeout and reschedule if needed unsigned long delay_ms = pending_peers->_sleep_ms; if (pending_peers->_timeout_ms > 0) { unsigned long elapsed = z_clock_elapsed_ms(&pending_peers->_start); if (elapsed >= (unsigned long)pending_peers->_timeout_ms) { return _z_add_peers_task_ready(pending_peers); } unsigned long remaining_ms = (unsigned long)pending_peers->_timeout_ms - elapsed; if (delay_ms > remaining_ms) { delay_ms = remaining_ms; } } _z_backoff_advance(&pending_peers->_sleep_ms); return _z_fut_fn_result_wake_up_after(delay_ms); } #endif ================================================ FILE: src/transport/multicast/lease.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/lease.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/multicast/lease.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 z_result_t _zp_multicast_send_join(_z_transport_multicast_t *ztm) { _z_conduit_sn_list_t next_sn; next_sn._is_qos = false; next_sn._val._plain._best_effort = ztm->_common._sn_tx_best_effort; next_sn._val._plain._reliable = ztm->_common._sn_tx_reliable; _z_id_t zid = _z_transport_common_get_session(&ztm->_common)->_local_zid; _z_transport_message_t jsm = _z_t_msg_make_join(Z_WHATAMI_PEER, Z_TRANSPORT_LEASE, zid, next_sn); return ztm->_send_f(&ztm->_common, &jsm); } z_result_t _zp_multicast_send_keep_alive(_z_transport_multicast_t *ztm) { _z_transport_message_t t_msg = _z_t_msg_make_keep_alive(); return ztm->_send_f(&ztm->_common, &t_msg); } _z_fut_fn_result_t _zp_multicast_failed_result(_z_transport_multicast_t *ztm, _z_executor_t *executor) { _z_session_t *session = _z_transport_common_get_session(&ztm->_common); #if Z_FEATURE_LIVELINESS == 1 && Z_FEATURE_SUBSCRIPTION == 1 _z_liveliness_subscription_undeclare_all(session); #endif _z_session_transport_mutex_lock(session); #if Z_FEATURE_AUTO_RECONNECT == 1 // Store weak session, to reuse for reconnection _z_session_weak_t zs = _z_session_weak_clone(&ztm->_common._session); #endif _z_transport_clear(&session->_tp); _z_session_transport_mutex_unlock(session); #if Z_FEATURE_AUTO_RECONNECT == 1 // Store weak session, to reuse for reconnection ztm->_common._state = _Z_TRANSPORT_STATE_RECONNECTING; ztm->_common._session = zs; _z_fut_t f = _z_fut_null(); f._fut_arg = &ztm->_common; f._fut_fn = _z_client_reopen_task_fn; f._destroy_fn = _z_client_reopen_task_drop; if (_z_fut_handle_is_null(_z_executor_spawn(executor, &f))) { _Z_ERROR("Failed to spawn client reopen task after transport failure."); ztm->_common._state = _Z_TRANSPORT_STATE_CLOSED; _z_session_weak_drop(&ztm->_common._session); return _z_fut_fn_result_ready(); } else { return _z_fut_fn_result_suspend(); } #else _ZP_UNUSED(executor); return _z_fut_fn_result_ready(); #endif } static _z_zint_t _z_get_minimum_lease(_z_transport_peer_multicast_slist_t *peers, _z_zint_t local_lease) { _z_zint_t ret = local_lease; _z_transport_peer_multicast_slist_t *it = peers; while (it != NULL) { _z_transport_peer_multicast_t *val = _z_transport_peer_multicast_slist_value(it); _z_zint_t lease = val->_lease; if (lease < ret) { ret = lease; } it = _z_transport_peer_multicast_slist_next(it); } return ret; } static bool _zp_multicast_peer_is_expired(const _z_transport_peer_multicast_t *target, const _z_transport_peer_multicast_t *peer) { _ZP_UNUSED(target); return !peer->common._received; } static void _zp_multicast_report_disconnected_events(_z_transport_multicast_t *ztm, _z_transport_peer_multicast_slist_t **dropped_peers) { if (dropped_peers == NULL || *dropped_peers == NULL) { return; } #if Z_FEATURE_CONNECTIVITY == 1 uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztm->_common, &mtu, &is_streamed, &is_reliable); #endif _z_session_t *s = _z_transport_common_get_session(&ztm->_common); _z_transport_peer_multicast_slist_t *it = *dropped_peers; while (it != NULL) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); _Z_INFO("Deleting peer because it has expired after %zums", peer->_lease); _z_interest_peer_disconnected(s, &peer->common); #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; _z_connectivity_peer_event_data_alias_from_common(&disconnected_peer, &peer->common); _z_connectivity_peer_disconnected(s, &disconnected_peer, true, mtu, is_streamed, is_reliable); #endif it = _z_transport_peer_multicast_slist_next(it); } _z_transport_peer_multicast_slist_free(dropped_peers); } _z_fut_fn_result_t _zp_multicast_lease_task_fn(void *ztm_arg, _z_executor_t *executor) { _ZP_UNUSED(executor); _z_transport_multicast_t *ztm = (_z_transport_multicast_t *)ztm_arg; if (ztm->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztm->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } _z_transport_peer_multicast_slist_t *dropped_peers = _z_transport_peer_multicast_slist_new(); _z_transport_peer_mutex_lock(&ztm->_common); ztm->_peers = _z_transport_peer_multicast_slist_extract_all_filter(ztm->_peers, &dropped_peers, _zp_multicast_peer_is_expired, NULL); _z_transport_peer_multicast_slist_t *curr_list = ztm->_peers; while (curr_list != NULL) { _z_transport_peer_multicast_t *curr_peer = _z_transport_peer_multicast_slist_value(curr_list); curr_peer->common._received = false; curr_list = _z_transport_peer_multicast_slist_next(curr_list); } unsigned long min_lease = (unsigned long)_z_get_minimum_lease(ztm->_peers, ztm->_common._lease); _z_transport_peer_mutex_unlock(&ztm->_common); _zp_multicast_report_disconnected_events(ztm, &dropped_peers); return _z_fut_fn_result_wake_up_after(min_lease); } _z_fut_fn_result_t _zp_multicast_keep_alive_task_fn(void *ztm_arg, _z_executor_t *executor) { _z_transport_multicast_t *ztm = (_z_transport_multicast_t *)ztm_arg; if (ztm->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztm->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } if (ztm->_common._transmitted == false) { if (_zp_multicast_send_keep_alive(ztm) < 0) { _Z_INFO("Send keep alive failed."); return _zp_multicast_failed_result(ztm, executor); } } ztm->_common._transmitted = false; _z_transport_peer_mutex_lock(&ztm->_common); unsigned long min_lease = (unsigned long)_z_get_minimum_lease(ztm->_peers, ztm->_common._lease); _z_transport_peer_mutex_unlock(&ztm->_common); return _z_fut_fn_result_wake_up_after(min_lease / Z_TRANSPORT_LEASE_EXPIRE_FACTOR); } _z_fut_fn_result_t _zp_multicast_send_join_task_fn(void *ztm_arg, _z_executor_t *executor) { _z_transport_multicast_t *ztm = (_z_transport_multicast_t *)ztm_arg; if (ztm->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztm->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } if (_zp_multicast_send_join(ztm) < 0) { _Z_INFO("Send join failed."); return _zp_multicast_failed_result(ztm, executor); } else { ztm->_common._transmitted = true; return _z_fut_fn_result_wake_up_after(Z_JOIN_INTERVAL); } } #endif // Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/transport/multicast/read.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/multicast/read.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/common/rx.h" #include "zenoh-pico/transport/multicast/lease.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_MULTICAST_TRANSPORT == 1 static z_result_t _zp_multicast_process_messages(_z_transport_multicast_t *ztm) { size_t to_read = 0; z_result_t ret = _z_multicast_recv_zbuf(ztm, &to_read); if (ret == _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES || ret == _Z_ERR_TRANSPORT_RX_FAILED) { return _Z_NO_DATA_PROCESSED; } else if (ret != _Z_RES_OK) { return ret; } // Wrap the main buffer to_read bytes _z_zbuf_t zbuf = _z_zbuf_view(&ztm->_common._zbuf, to_read); while (_z_zbuf_len(&zbuf) > 0) { // Decode one session message _z_transport_message_t t_msg; ret = _z_transport_message_decode(&t_msg, &zbuf); if (ret != _Z_RES_OK) { _Z_ERROR("Connection closed due to malformed message: %d", ret); break; } ret = _z_multicast_handle_transport_message(ztm, &t_msg, &ztm->_zbuf_addr); if (ret != _Z_RES_OK) { _Z_ERROR("Dropping message due to processing error: %d", ret); break; } } // Move the read position of the read buffer _z_zbuf_set_rpos(&ztm->_common._zbuf, _z_zbuf_get_rpos(&ztm->_common._zbuf) + to_read); if (_z_multicast_update_rx_buffer(ztm) != _Z_RES_OK) { _Z_ERROR("Connection closed due to lack of memory to allocate rx buffer"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return ret; } z_result_t _zp_multicast_read(_z_transport_multicast_t *ztm, bool single_read) { // Read & process a single message if (single_read) { _z_transport_message_t t_msg; _Z_RETURN_IF_ERR(_z_multicast_recv_t_msg(ztm, &t_msg)); _Z_CLEAN_RETURN_IF_ERR(_z_multicast_handle_transport_message(ztm, &t_msg, &ztm->_zbuf_addr), _z_t_msg_clear(&t_msg)); _z_t_msg_clear(&t_msg); return _z_multicast_update_rx_buffer(ztm); } else { return _zp_multicast_process_messages(ztm); } } _z_fut_fn_result_t _zp_multicast_read_task_fn(void *ztm_arg, _z_executor_t *executor) { _z_transport_multicast_t *ztm = (_z_transport_multicast_t *)ztm_arg; if (ztm->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztm->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } if (_zp_multicast_process_messages(ztm) < _Z_RES_OK) { // TODO: report failure and disconnect ? _Z_WARN("Multicast read task failed"); return _zp_multicast_failed_result(ztm, executor); } else { return _z_fut_fn_result_continue(); } } #endif ================================================ FILE: src/transport/multicast/rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/rx.h" #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_MULTICAST_TRANSPORT == 1 z_result_t _z_multicast_recv_zbuf(_z_transport_multicast_t *ztm, size_t *to_read) { z_result_t ret = _Z_RES_OK; do { // Read bytes from socket to the main buffer switch (ztm->_common._link->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: if (_z_zbuf_len(&ztm->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_link_recv_zbuf(ztm->_common._link, &ztm->_common._zbuf, &ztm->_zbuf_addr); if (_z_zbuf_len(&ztm->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_zbuf_compact(&ztm->_common._zbuf); ret = _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES; break; } } // Get stream size *to_read = _z_read_stream_size(&ztm->_common._zbuf); // Read data if (_z_zbuf_len(&ztm->_common._zbuf) < *to_read) { _z_link_recv_zbuf(ztm->_common._link, &ztm->_common._zbuf, NULL); if (_z_zbuf_len(&ztm->_common._zbuf) < *to_read) { _z_zbuf_set_rpos(&ztm->_common._zbuf, _z_zbuf_get_rpos(&ztm->_common._zbuf) - _Z_MSG_LEN_ENC_SIZE); _z_zbuf_compact(&ztm->_common._zbuf); ret = _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES; break; } } break; // Datagram capable links case Z_LINK_CAP_FLOW_DATAGRAM: if (_z_zbuf_len(&ztm->_common._zbuf) == 0) { _z_zbuf_compact(&ztm->_common._zbuf); *to_read = _z_link_recv_zbuf(ztm->_common._link, &ztm->_common._zbuf, &ztm->_zbuf_addr); if (*to_read == SIZE_MAX) { ret = _Z_ERR_TRANSPORT_RX_FAILED; } } else { *to_read = _z_zbuf_len(&ztm->_common._zbuf); } break; default: break; } } while (false); // The 1-iteration loop to use continue to break the entire loop on error return ret; } z_result_t _z_multicast_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg) { _Z_DEBUG(">> recv session msg"); size_t to_read = 0; z_result_t ret = _z_multicast_recv_zbuf(ztm, &to_read); if (ret == _Z_RES_OK) { _Z_DEBUG(">> \t transport_message_decode: %ju", (uintmax_t)_z_zbuf_len(&ztm->_common._zbuf)); // Wrap the main buffer to_read bytes _z_zbuf_t zbuf = _z_zbuf_view(&ztm->_common._zbuf, to_read); ret = _z_transport_message_decode(t_msg, &zbuf); if (ret == _Z_RES_OK) { _z_zbuf_set_rpos(&ztm->_common._zbuf, _z_zbuf_get_rpos(&ztm->_common._zbuf) + _z_zbuf_get_rpos(&zbuf)); } else { _Z_ERROR("Malformed transport message: %d", ret); _z_zbuf_set_rpos(&ztm->_common._zbuf, _z_zbuf_get_rpos(&ztm->_common._zbuf) + to_read); } } return ret; } #else z_result_t _z_multicast_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg) { _ZP_UNUSED(ztm); _ZP_UNUSED(t_msg); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif // Z_FEATURE_MULTICAST_TRANSPORT == 1 #if Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 #if Z_FEATURE_CONNECTIVITY == 1 static z_result_t _z_multicast_remote_addr_to_endpoint(const _z_transport_multicast_t *ztm, const _z_slice_t *remote_addr, _z_string_t *endpoint) { char addr[96] = {0}; _z_locator_t locator; const uint8_t *bytes = remote_addr->start; size_t address_len = 0; uint16_t port = 0; *endpoint = _z_string_null(); if (ztm == NULL || ztm->_common._link == NULL || remote_addr == NULL || bytes == NULL) { _Z_ERROR_RETURN(_Z_ERR_INVALID); } if (remote_addr->len == sizeof(uint32_t) + sizeof(uint16_t)) { address_len = sizeof(uint32_t); port = ((uint16_t)bytes[4] << 8) | (uint16_t)bytes[5]; } else if (remote_addr->len == 16 + sizeof(uint16_t)) { address_len = 16; port = ((uint16_t)bytes[16] << 8) | (uint16_t)bytes[17]; } else { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _Z_RETURN_IF_ERR(_z_ip_port_to_endpoint(bytes, address_len, port, addr, sizeof(addr))); _z_locator_init(&locator); if (_z_string_check(&ztm->_common._link->_endpoint._locator._protocol)) { locator._protocol = _z_string_alias(ztm->_common._link->_endpoint._locator._protocol); } else { locator._protocol = _z_string_alias_str("udp"); } locator._address = _z_string_alias_str(addr); *endpoint = _z_locator_to_string(&locator); _z_locator_clear(&locator); if (!_z_string_check(endpoint)) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } #endif static _z_transport_peer_multicast_t *_z_find_peer_entry(_z_transport_peer_multicast_slist_t *l, _z_slice_t *remote_addr) { _z_transport_peer_multicast_t *ret = NULL; _z_transport_peer_multicast_slist_t *xs = l; for (; xs != NULL; xs = _z_transport_peer_multicast_slist_next(xs)) { _z_transport_peer_multicast_t *val = _z_transport_peer_multicast_slist_value(xs); if (val->_remote_addr.len != remote_addr->len) { continue; } if (memcmp(val->_remote_addr.start, remote_addr->start, remote_addr->len) == 0) { ret = val; } } return ret; } static z_result_t _z_multicast_handle_frame(_z_transport_multicast_t *ztm, uint8_t header, _z_t_msg_frame_t *msg, _z_transport_peer_multicast_t *entry) { // Check peer if (entry == NULL) { _Z_INFO("Dropping _Z_FRAME from unknown peer"); _z_t_msg_frame_clear(msg); return _Z_RES_OK; } // Note that we receive data from peer entry->common._received = true; z_reliability_t tmsg_reliability; // Check if the SN is correct if (_Z_HAS_FLAG(header, _Z_FLAG_T_FRAME_R)) { tmsg_reliability = Z_RELIABILITY_RELIABLE; // @TODO: amend once reliability is in place. For the time being only // monotonic SNs are ensured if (_z_sn_precedes(entry->_sn_res, entry->_sn_rx_sns._val._plain._reliable, msg->_sn)) { entry->_sn_rx_sns._val._plain._reliable = msg->_sn; } else { #if Z_FEATURE_FRAGMENTATION == 1 entry->common._state_reliable = _Z_DBUF_STATE_NULL; _z_wbuf_clear(&entry->common._dbuf_reliable); #endif _Z_INFO("Reliable message dropped because it is out of order"); _z_t_msg_frame_clear(msg); return _Z_RES_OK; } } else { tmsg_reliability = Z_RELIABILITY_BEST_EFFORT; if (_z_sn_precedes(entry->_sn_res, entry->_sn_rx_sns._val._plain._best_effort, msg->_sn)) { entry->_sn_rx_sns._val._plain._best_effort = msg->_sn; } else { #if Z_FEATURE_FRAGMENTATION == 1 entry->common._state_best_effort = _Z_DBUF_STATE_NULL; _z_wbuf_clear(&entry->common._dbuf_best_effort); #endif _Z_INFO("Best effort message dropped because it is out of order"); _z_t_msg_frame_clear(msg); return _Z_RES_OK; } } // Handle all the zenoh message, one by one // From this point, memory cleaning must be handled by the network message layer _z_network_message_t curr_nmsg = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); while (_z_zbuf_len(msg->_payload) > 0) { _Z_RETURN_IF_ERR(_z_network_message_decode(&curr_nmsg, msg->_payload, &arcs, (uintptr_t)&entry->common)); curr_nmsg._reliability = tmsg_reliability; _Z_RETURN_IF_ERR(_z_handle_network_message(&ztm->_common, &curr_nmsg, &entry->common)); } return _Z_RES_OK; } static z_result_t _z_multicast_handle_fragment_inner(_z_transport_multicast_t *ztm, uint8_t header, _z_t_msg_fragment_t *msg, _z_transport_peer_multicast_t *entry) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_FRAGMENTATION == 1 // Check peer if (entry == NULL) { _Z_INFO("Dropping Z_FRAGMENT from unknown peer"); return _Z_RES_OK; } // Note that we receive data from the peer entry->common._received = true; _z_wbuf_t *dbuf; uint8_t *dbuf_state; z_reliability_t tmsg_reliability; bool consecutive; // Select the right defragmentation buffer if (_Z_HAS_FLAG(header, _Z_FLAG_T_FRAME_R)) { tmsg_reliability = Z_RELIABILITY_RELIABLE; // Check SN // @TODO: amend once reliability is in place. For the time being only // monotonic SNs are ensured if (_z_sn_precedes(entry->_sn_res, entry->_sn_rx_sns._val._plain._reliable, msg->_sn)) { consecutive = _z_sn_consecutive(entry->_sn_res, entry->_sn_rx_sns._val._plain._reliable, msg->_sn); entry->_sn_rx_sns._val._plain._reliable = msg->_sn; dbuf = &entry->common._dbuf_reliable; dbuf_state = &entry->common._state_reliable; } else { _z_wbuf_clear(&entry->common._dbuf_reliable); entry->common._state_reliable = _Z_DBUF_STATE_NULL; _Z_INFO("Reliable message dropped because it is out of order"); return _Z_RES_OK; } } else { tmsg_reliability = Z_RELIABILITY_BEST_EFFORT; // Check SN if (_z_sn_precedes(entry->_sn_res, entry->_sn_rx_sns._val._plain._best_effort, msg->_sn)) { consecutive = _z_sn_consecutive(entry->_sn_res, entry->_sn_rx_sns._val._plain._best_effort, msg->_sn); entry->_sn_rx_sns._val._plain._best_effort = msg->_sn; dbuf = &entry->common._dbuf_best_effort; dbuf_state = &entry->common._state_best_effort; } else { _z_wbuf_clear(&entry->common._dbuf_best_effort); entry->common._state_best_effort = _Z_DBUF_STATE_NULL; _Z_INFO("Best effort message dropped because it is out of order"); return _Z_RES_OK; } } if (!consecutive && (_z_wbuf_len(dbuf) > 0)) { _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; _Z_INFO("Defragmentation buffer dropped because non-consecutive fragments received"); return _Z_RES_OK; } // Handle fragment markers if (_Z_PATCH_HAS_FRAGMENT_MARKERS(entry->common._patch)) { if (msg->first) { _z_wbuf_reset(dbuf); } else if (_z_wbuf_len(dbuf) == 0) { _Z_INFO("First fragment received without the first marker"); return _Z_RES_OK; } if (msg->drop) { _z_wbuf_reset(dbuf); return _Z_RES_OK; } } // Allocate buffer if needed if (*dbuf_state == _Z_DBUF_STATE_NULL) { *dbuf = _z_wbuf_make(Z_FRAG_MAX_SIZE, false); if (_z_wbuf_capacity(dbuf) != Z_FRAG_MAX_SIZE) { _Z_ERROR("Not enough memory to allocate peer defragmentation buffer"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *dbuf_state = _Z_DBUF_STATE_INIT; } // Process fragment data if (*dbuf_state == _Z_DBUF_STATE_INIT) { // Check overflow if ((_z_wbuf_len(dbuf) + msg->_payload.len) > Z_FRAG_MAX_SIZE) { *dbuf_state = _Z_DBUF_STATE_OVERFLOW; } else { // Fill buffer _z_wbuf_write_bytes(dbuf, msg->_payload.start, 0, msg->_payload.len); } } // Process final fragment if (!_Z_HAS_FLAG(header, _Z_FLAG_T_FRAGMENT_M)) { // Drop message if it exceeds the fragmentation size if (*dbuf_state == _Z_DBUF_STATE_OVERFLOW) { _Z_INFO("Fragment dropped because defragmentation buffer has overflown"); _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; return _Z_RES_OK; } // Convert the defragmentation buffer into a decoding buffer _z_zbuf_t zbf = _z_wbuf_moved_as_zbuf(dbuf); if (_z_zbuf_capacity(&zbf) == 0) { _Z_ERROR("Failed to convert defragmentation buffer into a decoding buffer!"); _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Decode message _z_zenoh_message_t zm = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); ret = _z_network_message_decode(&zm, &zbf, &arcs, (uintptr_t)&entry->common); zm._reliability = tmsg_reliability; if (ret == _Z_RES_OK) { // Memory clear of the network message data must be handled by the network message layer _z_handle_network_message(&ztm->_common, &zm, &entry->common); } else { _Z_INFO("Failed to decode defragmented message"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } // Free the decoding buffer _z_zbuf_clear(&zbf); *dbuf_state = _Z_DBUF_STATE_NULL; } #else _ZP_UNUSED(ztm); _ZP_UNUSED(header); _ZP_UNUSED(msg); _ZP_UNUSED(entry); _Z_INFO("Fragment dropped because fragmentation feature is deactivated"); #endif return ret; } static z_result_t _z_multicast_handle_fragment(_z_transport_multicast_t *ztm, uint8_t header, _z_t_msg_fragment_t *msg, _z_transport_peer_multicast_t *entry) { z_result_t ret = _z_multicast_handle_fragment_inner(ztm, header, msg, entry); _z_t_msg_fragment_clear(msg); return ret; } static z_result_t _z_multicast_handle_join_inner(_z_transport_multicast_t *ztm, _z_slice_t *addr, _z_t_msg_join_t *msg, _z_transport_peer_multicast_t *entry) { // Check proto version if (msg->_version != Z_PROTO_VERSION) { return _Z_RES_OK; } // Check peer if (entry == NULL) { // New peer // If the new node has less representing capabilities then we can't communicate if ((msg->_seq_num_res != Z_SN_RESOLUTION) || (msg->_req_id_res != Z_REQ_RESOLUTION) || (msg->_batch_size != Z_BATCH_MULTICAST_SIZE)) { _Z_INFO("Couldn't accept peer because distant node is incompatible config wise."); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION); } // Initialize entry ztm->_peers = _z_transport_peer_multicast_slist_push_empty(ztm->_peers); entry = _z_transport_peer_multicast_slist_value(ztm->_peers); entry->_sn_res = _z_sn_max(msg->_seq_num_res); entry->_remote_addr = _z_slice_duplicate(addr); _z_conduit_sn_list_copy(&entry->_sn_rx_sns, &msg->_next_sn); _z_conduit_sn_list_decrement(entry->_sn_res, &entry->_sn_rx_sns); // Update lease time (set as ms during) entry->_lease = msg->_lease; entry->common._remote_zid = msg->_zid; entry->common._remote_whatami = msg->_whatami; entry->common._received = true; entry->common._remote_resources = NULL; #if Z_FEATURE_CONNECTIVITY == 1 entry->common._link_src = _z_string_null(); entry->common._link_dst = _z_string_null(); if (ztm->_common._link != NULL) { entry->common._link_src = _z_endpoint_to_string(&ztm->_common._link->_endpoint); (void)_z_multicast_remote_addr_to_endpoint(ztm, addr, &entry->common._link_dst); } #endif #if Z_FEATURE_FRAGMENTATION == 1 entry->common._patch = msg->_patch < _Z_CURRENT_PATCH ? msg->_patch : _Z_CURRENT_PATCH; entry->common._state_reliable = _Z_DBUF_STATE_NULL; entry->common._state_best_effort = _Z_DBUF_STATE_NULL; entry->common._dbuf_reliable = _z_wbuf_null(); entry->common._dbuf_best_effort = _z_wbuf_null(); #endif #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t connected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztm->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&connected_peer, &entry->common); _z_transport_peer_mutex_unlock(&ztm->_common); _z_connectivity_peer_connected(_z_transport_common_get_session(&ztm->_common), &connected_peer, true, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&connected_peer); _z_transport_peer_mutex_lock(&ztm->_common); #endif } else { // Existing peer // Note that we receive data from the peer entry->common._received = true; // Check representing capabilities if ((msg->_seq_num_res != Z_SN_RESOLUTION) || (msg->_req_id_res != Z_REQ_RESOLUTION) || (msg->_batch_size != Z_BATCH_MULTICAST_SIZE)) { #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztm->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&disconnected_peer, &entry->common); #endif // TODO: cleanup here should also be done on mappings/subs/etc... _z_transport_peer_multicast_slist_drop_first_filter(ztm->_peers, _z_transport_peer_multicast_eq, entry); #if Z_FEATURE_CONNECTIVITY == 1 _z_transport_peer_mutex_unlock(&ztm->_common); _z_connectivity_peer_disconnected(_z_transport_common_get_session(&ztm->_common), &disconnected_peer, true, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&disconnected_peer); _z_transport_peer_mutex_lock(&ztm->_common); #endif return _Z_RES_OK; } // Update SNs _z_conduit_sn_list_copy(&entry->_sn_rx_sns, &msg->_next_sn); _z_conduit_sn_list_decrement(entry->_sn_res, &entry->_sn_rx_sns); // Update lease time (set as ms during) entry->_lease = msg->_lease; } return _Z_RES_OK; } static z_result_t _z_multicast_handle_join(_z_transport_multicast_t *ztm, _z_slice_t *addr, _z_t_msg_join_t *msg, _z_transport_peer_multicast_t *entry) { z_result_t ret = _z_multicast_handle_join_inner(ztm, addr, msg, entry); _z_t_msg_join_clear(msg); return ret; } z_result_t _z_multicast_handle_transport_message(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr) { z_result_t ret = _Z_RES_OK; _z_transport_peer_mutex_lock(&ztm->_common); // Mark the session that we have received data from this peer _z_transport_peer_multicast_t *entry = _z_find_peer_entry(ztm->_peers, addr); switch (_Z_MID(t_msg->_header)) { case _Z_MID_T_FRAME: { _Z_DEBUG("Received _Z_FRAME message"); ret = _z_multicast_handle_frame(ztm, t_msg->_header, &t_msg->_body._frame, entry); break; } case _Z_MID_T_FRAGMENT: _Z_DEBUG("Received Z_FRAGMENT message"); ret = _z_multicast_handle_fragment(ztm, t_msg->_header, &t_msg->_body._fragment, entry); break; case _Z_MID_T_KEEP_ALIVE: { _Z_DEBUG("Received _Z_KEEP_ALIVE message"); if (entry != NULL) { entry->common._received = true; } _z_t_msg_keep_alive_clear(&t_msg->_body._keep_alive); break; } case _Z_MID_T_INIT: { // Do nothing, multicast transports are not expected to handle INIT messages _z_t_msg_init_clear(&t_msg->_body._init); break; } case _Z_MID_T_OPEN: { // Do nothing, multicast transports are not expected to handle OPEN messages _z_t_msg_open_clear(&t_msg->_body._open); break; } case _Z_MID_T_JOIN: { _Z_DEBUG("Received _Z_JOIN message"); ret = _z_multicast_handle_join(ztm, addr, &t_msg->_body._join, entry); break; } case _Z_MID_T_CLOSE: { _Z_INFO("Closing connection as requested by the remote peer"); if (entry != NULL) { #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztm->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&disconnected_peer, &entry->common); #endif ztm->_peers = _z_transport_peer_multicast_slist_drop_first_filter( ztm->_peers, _z_transport_peer_multicast_eq, entry); #if Z_FEATURE_CONNECTIVITY == 1 _z_transport_peer_mutex_unlock(&ztm->_common); _z_connectivity_peer_disconnected(_z_transport_common_get_session(&ztm->_common), &disconnected_peer, true, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&disconnected_peer); _z_transport_peer_mutex_lock(&ztm->_common); #endif } _z_t_msg_close_clear(&t_msg->_body._close); break; } default: { _Z_ERROR("Unknown session message ID"); _z_t_msg_clear(t_msg); break; } } _z_transport_peer_mutex_unlock(&ztm->_common); return ret; } z_result_t _z_multicast_update_rx_buffer(_z_transport_multicast_t *ztm) { // Check if user or defragment buffer took ownership of buffer if (_z_zbuf_get_ref_count(&ztm->_common._zbuf) != 1) { // Allocate a new buffer _z_zbuf_t new_zbuf = _z_zbuf_make(Z_BATCH_MULTICAST_SIZE); if (_z_zbuf_capacity(&new_zbuf) != Z_BATCH_MULTICAST_SIZE) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Recopy leftover bytes size_t leftovers = _z_zbuf_len(&ztm->_common._zbuf); if (leftovers > 0) { _z_zbuf_copy_bytes(&new_zbuf, &ztm->_common._zbuf); } // Drop buffer & update _z_zbuf_clear(&ztm->_common._zbuf); ztm->_common._zbuf = new_zbuf; } return _Z_RES_OK; } #else z_result_t _z_multicast_handle_transport_message(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr) { _ZP_UNUSED(ztm); _ZP_UNUSED(t_msg); _ZP_UNUSED(addr); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif // Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/transport/multicast/transport.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/transport/common/transport.h" #include #include #include #include #include #include "zenoh-pico/link/link.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/raweth/tx.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 z_result_t _z_multicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_multicast_establish_param_t *param) { z_result_t ret = _Z_RES_OK; // Transport specific information _z_transport_multicast_t *ztm = NULL; switch (zl->_cap._transport) { case Z_LINK_CAP_TRANSPORT_MULTICAST: zt->_type = _Z_TRANSPORT_MULTICAST_TYPE; ztm = &zt->_transport._multicast; ztm->_send_f = _z_transport_tx_send_t_msg_wrapper; break; case Z_LINK_CAP_TRANSPORT_RAWETH: zt->_type = _Z_TRANSPORT_RAWETH_TYPE; ztm = &zt->_transport._raweth; ztm->_send_f = _z_raweth_send_t_msg; break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Initialize persistent address buffer ztm->_zbuf_addr = _z_slice_alias_buf(ztm->_zbuf_addr_buf, sizeof(ztm->_zbuf_addr_buf)); // Initialize batching data #if Z_FEATURE_BATCHING == 1 ztm->_common._batch_state = _Z_BATCHING_IDLE; ztm->_common._batch_count = 0; #endif #if Z_FEATURE_MULTI_THREAD == 1 // Initialize the mutexes ret = _z_mutex_init(&ztm->_common._mutex_tx); if (ret == _Z_RES_OK) { ret = _z_mutex_rec_init(&ztm->_common._mutex_peer); if (ret != _Z_RES_OK) { _z_mutex_drop(&ztm->_common._mutex_tx); } } #endif // Z_FEATURE_MULTI_THREAD == 1 // Initialize the read and write buffers if (ret == _Z_RES_OK) { uint16_t mtu = (zl->_mtu < Z_BATCH_MULTICAST_SIZE) ? zl->_mtu : Z_BATCH_MULTICAST_SIZE; ztm->_common._wbuf = _z_wbuf_make(mtu, false); ztm->_common._zbuf = _z_zbuf_make(Z_BATCH_MULTICAST_SIZE); // Clean up the buffers if one of them failed to be allocated if ((_z_wbuf_capacity(&ztm->_common._wbuf) != mtu) || (_z_zbuf_capacity(&ztm->_common._zbuf) != Z_BATCH_MULTICAST_SIZE)) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); ret = _Z_ERR_SYSTEM_OUT_OF_MEMORY; _Z_ERROR("Not enough memory to allocate transport tx rx buffers!"); #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&ztm->_common._mutex_tx); _z_mutex_rec_drop(&ztm->_common._mutex_peer); #endif // Z_FEATURE_MULTI_THREAD == 1 _z_wbuf_clear(&ztm->_common._wbuf); _z_zbuf_clear(&ztm->_common._zbuf); } } if (ret == _Z_RES_OK) { // Set default SN resolution ztm->_common._sn_res = _z_sn_max(param->_seq_num_res); // The initial SN at TX side ztm->_common._sn_tx_reliable = param->_initial_sn_tx._val._plain._reliable; ztm->_common._sn_tx_best_effort = param->_initial_sn_tx._val._plain._best_effort; // Initialize peer list ztm->_peers = _z_transport_peer_multicast_slist_new(); ztm->_common._lease = Z_TRANSPORT_LEASE; // Notifiers ztm->_common._transmitted = false; // Transport link for multicast ztm->_common._link = zl; } return ret; } z_result_t _z_multicast_open_peer(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { z_result_t ret = _Z_RES_OK; _z_zint_t initial_sn_tx = 0; z_random_fill(&initial_sn_tx, sizeof(initial_sn_tx)); initial_sn_tx = initial_sn_tx & !_z_sn_modulo_mask(Z_SN_RESOLUTION); _z_conduit_sn_list_t next_sn; next_sn._is_qos = false; next_sn._val._plain._best_effort = initial_sn_tx; next_sn._val._plain._reliable = initial_sn_tx; _z_id_t zid = *local_zid; _z_transport_message_t jsm = _z_t_msg_make_join(Z_WHATAMI_PEER, Z_TRANSPORT_LEASE, zid, next_sn); // Encode and send the message _Z_DEBUG("Sending Z_JOIN message"); switch (zl->_cap._transport) { case Z_LINK_CAP_TRANSPORT_MULTICAST: ret = _z_link_send_t_msg(zl, &jsm, NULL); break; case Z_LINK_CAP_TRANSPORT_RAWETH: ret = _z_raweth_link_send_t_msg(zl, &jsm); break; default: _Z_ERROR_RETURN(_Z_ERR_GENERIC); } _z_t_msg_clear(&jsm); if (ret == _Z_RES_OK) { param->_seq_num_res = jsm._body._join._seq_num_res; param->_initial_sn_tx = next_sn; } return ret; } z_result_t _z_multicast_open_client(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { _ZP_UNUSED(param); _ZP_UNUSED(zl); _ZP_UNUSED(local_zid); _Z_ERROR_LOG(_Z_ERR_CONFIG_UNSUPPORTED_CLIENT_MULTICAST); z_result_t ret = _Z_ERR_CONFIG_UNSUPPORTED_CLIENT_MULTICAST; // @TODO: not implemented return ret; } z_result_t _z_multicast_send_close(_z_transport_multicast_t *ztm, uint8_t reason, bool link_only) { z_result_t ret = _Z_RES_OK; // Send and clear message _z_transport_message_t cm = _z_t_msg_make_close(reason, link_only); ret = ztm->_send_f(&ztm->_common, &cm); _z_t_msg_clear(&cm); return ret; } z_result_t _z_multicast_transport_close(_z_transport_multicast_t *ztm, uint8_t reason) { return _z_multicast_send_close(ztm, reason, false); } void _z_multicast_transport_clear(_z_transport_multicast_t *ztm) { _z_transport_peer_multicast_slist_free(&ztm->_peers); _z_transport_common_clear( &ztm->_common); // free common in the very end, as peers might access the link data in common while being freed _z_slice_clear(&ztm->_zbuf_addr); } #else z_result_t _z_multicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_multicast_establish_param_t *param) { _ZP_UNUSED(zt); _ZP_UNUSED(zl); _ZP_UNUSED(param); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_multicast_open_peer(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { _ZP_UNUSED(param); _ZP_UNUSED(zl); _ZP_UNUSED(local_zid); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_multicast_open_client(_z_transport_multicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { _ZP_UNUSED(param); _ZP_UNUSED(zl); _ZP_UNUSED(local_zid); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_multicast_send_close(_z_transport_multicast_t *ztm, uint8_t reason, bool link_only) { _ZP_UNUSED(ztm); _ZP_UNUSED(reason); _ZP_UNUSED(link_only); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_multicast_transport_close(_z_transport_multicast_t *ztm, uint8_t reason) { _ZP_UNUSED(ztm); _ZP_UNUSED(reason); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } void _z_multicast_transport_clear(_z_transport_multicast_t *ztm) { _ZP_UNUSED(ztm); } #endif // Z_FEATURE_MULTICAST_TRANSPORT == 1 || Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/transport/multicast.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/transport/multicast.h" #include #include #include #include #include #include "zenoh-pico/link/link.h" #include "zenoh-pico/transport/common/lease.h" #include "zenoh-pico/transport/common/read.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/uuid.h" #if Z_FEATURE_MULTICAST_TRANSPORT == 1 void _zp_multicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback) { void *ctx = callback->context; _z_transport_peer_multicast_slist_t *l = zt->_transport._multicast._peers; for (; l != NULL; l = _z_transport_peer_multicast_slist_next(l)) { _z_transport_peer_multicast_t *val = _z_transport_peer_multicast_slist_value(l); z_id_t id = val->common._remote_zid; callback->call(&id, ctx); } } void _zp_multicast_info_session(const _z_transport_t *zt, _z_config_t *ps) { _z_transport_peer_multicast_slist_t *xs = zt->_transport._multicast._peers; while (xs != NULL) { _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(xs); _z_string_t remote_zid_str = _z_id_to_string(&peer->common._remote_zid); _zp_config_insert_string(ps, Z_INFO_PEER_PID_KEY, &remote_zid_str); _z_string_clear(&remote_zid_str); xs = _z_transport_peer_multicast_slist_next(xs); } } #else void _zp_multicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback) { _ZP_UNUSED(zt); _ZP_UNUSED(callback); } void _zp_multicast_info_session(const _z_transport_t *zt, _z_config_t *ps) { _ZP_UNUSED(zt); _ZP_UNUSED(ps); } #endif // Z_FEATURE_MULTICAST_TRANSPORT == 1 ================================================ FILE: src/transport/peer.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/link/transport/socket.h" #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/transport/tls_stream.h" #endif #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/utils.h" #if Z_FEATURE_CONNECTIVITY == 1 static z_result_t _z_transport_make_endpoint(const _z_string_t *protocol, const char *address, _z_string_t *out) { _z_locator_t locator; *out = _z_string_null(); if (address == NULL || address[0] == '\0') { _Z_ERROR_RETURN(_Z_ERR_INVALID); } _z_locator_init(&locator); if (protocol != NULL && _z_string_check(protocol)) { locator._protocol = _z_string_alias(*protocol); } else { locator._protocol = _z_string_alias_str("tcp"); } locator._address = _z_string_alias_str(address); *out = _z_locator_to_string(&locator); _z_locator_clear(&locator); if (!_z_string_check(out)) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } #endif void _z_transport_peer_common_clear(_z_transport_peer_common_t *src) { #if Z_FEATURE_CONNECTIVITY == 1 _z_string_clear(&src->_link_src); _z_string_clear(&src->_link_dst); #endif #if Z_FEATURE_FRAGMENTATION == 1 _z_wbuf_clear(&src->_dbuf_reliable); _z_wbuf_clear(&src->_dbuf_best_effort); #endif src->_remote_zid = _z_id_empty(); _z_resource_slist_free(&src->_remote_resources); } void _z_transport_peer_common_copy(_z_transport_peer_common_t *dst, const _z_transport_peer_common_t *src) { #if Z_FEATURE_CONNECTIVITY == 1 dst->_link_src = _z_string_null(); dst->_link_dst = _z_string_null(); (void)_z_string_copy(&dst->_link_src, &src->_link_src); if (_z_string_copy(&dst->_link_dst, &src->_link_dst) != _Z_RES_OK) { _z_string_clear(&dst->_link_src); } #endif #if Z_FEATURE_FRAGMENTATION == 1 dst->_state_reliable = src->_state_reliable; dst->_state_best_effort = src->_state_best_effort; _z_wbuf_copy(&dst->_dbuf_reliable, &src->_dbuf_reliable); _z_wbuf_copy(&dst->_dbuf_best_effort, &src->_dbuf_best_effort); dst->_patch = src->_patch; #endif dst->_remote_resources = NULL; dst->_received = src->_received; dst->_remote_zid = src->_remote_zid; dst->_remote_whatami = src->_remote_whatami; } #if Z_FEATURE_CONNECTIVITY == 1 void _z_connectivity_peer_event_data_clear(_z_connectivity_peer_event_data_t *event_data) { if (event_data == NULL) { return; } if (event_data->_owns_endpoints) { _z_string_clear(&event_data->_link_src); _z_string_clear(&event_data->_link_dst); } *event_data = (_z_connectivity_peer_event_data_t){0}; } void _z_connectivity_peer_event_data_copy_from_common(_z_connectivity_peer_event_data_t *dst, const _z_transport_peer_common_t *src) { *dst = (_z_connectivity_peer_event_data_t){0}; dst->_link_src = _z_string_null(); dst->_link_dst = _z_string_null(); dst->_remote_zid = src->_remote_zid; dst->_remote_whatami = src->_remote_whatami; dst->_owns_endpoints = true; (void)_z_string_copy(&dst->_link_src, &src->_link_src); if (_z_string_copy(&dst->_link_dst, &src->_link_dst) != _Z_RES_OK) { _z_string_clear(&dst->_link_src); } } void _z_connectivity_peer_event_data_alias_from_common(_z_connectivity_peer_event_data_t *dst, const _z_transport_peer_common_t *src) { *dst = (_z_connectivity_peer_event_data_t){ ._remote_zid = src->_remote_zid, ._remote_whatami = src->_remote_whatami, ._link_src = src->_link_src, ._link_dst = src->_link_dst, ._owns_endpoints = false, }; } #endif bool _z_transport_peer_common_eq(const _z_transport_peer_common_t *left, const _z_transport_peer_common_t *right) { return _z_id_eq(&left->_remote_zid, &right->_remote_zid); } void _z_transport_peer_multicast_clear(_z_transport_peer_multicast_t *src) { _z_slice_clear(&src->_remote_addr); _z_transport_peer_common_clear(&src->common); } void _z_transport_peer_multicast_copy(_z_transport_peer_multicast_t *dst, const _z_transport_peer_multicast_t *src) { dst->_sn_res = src->_sn_res; _z_conduit_sn_list_copy(&dst->_sn_rx_sns, &src->_sn_rx_sns); dst->_lease = src->_lease; _z_slice_copy(&dst->_remote_addr, &src->_remote_addr); _z_transport_peer_common_copy(&dst->common, &src->common); } size_t _z_transport_peer_multicast_size(const _z_transport_peer_multicast_t *src) { _ZP_UNUSED(src); return sizeof(_z_transport_peer_multicast_t); } bool _z_transport_peer_multicast_eq(const _z_transport_peer_multicast_t *left, const _z_transport_peer_multicast_t *right) { return _z_transport_peer_common_eq(&left->common, &right->common); } void _z_transport_peer_unicast_clear(_z_transport_peer_unicast_t *src) { _z_zbuf_clear(&src->flow_buff); if (src->_owns_socket) { #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&src->_socket); #endif _z_socket_close(&src->_socket); } _z_transport_peer_common_clear(&src->common); } void _z_transport_peer_unicast_copy(_z_transport_peer_unicast_t *dst, const _z_transport_peer_unicast_t *src) { dst->_sn_rx_reliable = src->_sn_rx_reliable; dst->_sn_rx_best_effort = src->_sn_rx_best_effort; dst->_socket = src->_socket; dst->_owns_socket = false; // Ownership is not copied dst->_pending = false; dst->flow_state = _Z_FLOW_STATE_INACTIVE; dst->flow_curr_size = 0; dst->flow_buff = _z_zbuf_null(); _z_transport_peer_common_copy(&dst->common, &src->common); } size_t _z_transport_peer_unicast_size(const _z_transport_peer_unicast_t *src) { _ZP_UNUSED(src); return sizeof(_z_transport_peer_unicast_t); } bool _z_transport_peer_unicast_eq(const _z_transport_peer_unicast_t *left, const _z_transport_peer_unicast_t *right) { return _z_transport_peer_common_eq(&left->common, &right->common); } z_result_t _z_transport_peer_unicast_add(_z_transport_unicast_t *ztu, _z_transport_unicast_establish_param_t *param, _z_sys_net_socket_t socket, bool owns_socket, _z_transport_peer_unicast_t **output_peer) { #if Z_FEATURE_CONNECTIVITY == 1 bool dispatch_connected_event = output_peer == NULL; _z_session_t *session = _z_transport_common_get_session(&ztu->_common); _z_connectivity_peer_event_data_t peer_event_data = {0}; char local_addr[160] = {0}; char remote_addr[160] = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; #endif _z_transport_peer_mutex_lock(&ztu->_common); // Create peer ztu->_peers = _z_transport_peer_unicast_slist_push_empty(ztu->_peers); if (ztu->_peers == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Fill peer data _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(ztu->_peers); peer->flow_state = _Z_FLOW_STATE_INACTIVE; peer->flow_curr_size = 0; peer->flow_buff = _z_zbuf_null(); peer->_pending = false; peer->_socket = socket; peer->_owns_socket = owns_socket; _z_zint_t initial_sn_rx = _z_sn_decrement(ztu->_common._sn_res, param->_initial_sn_rx); peer->_sn_rx_reliable = initial_sn_rx; peer->_sn_rx_best_effort = initial_sn_rx; peer->common._remote_zid = param->_remote_zid; peer->common._remote_whatami = param->_remote_whatami; peer->common._received = true; peer->common._remote_resources = NULL; #if Z_FEATURE_CONNECTIVITY == 1 peer->common._link_src = _z_string_null(); peer->common._link_dst = _z_string_null(); #endif #if Z_FEATURE_FRAGMENTATION == 1 peer->common._patch = param->_patch < _Z_CURRENT_PATCH ? param->_patch : _Z_CURRENT_PATCH; peer->common._state_reliable = _Z_DBUF_STATE_NULL; peer->common._state_best_effort = _Z_DBUF_STATE_NULL; peer->common._dbuf_reliable = _z_wbuf_null(); peer->common._dbuf_best_effort = _z_wbuf_null(); #endif #if Z_FEATURE_CONNECTIVITY == 1 if (ztu->_common._link != NULL) { mtu = ztu->_common._link->_mtu; is_streamed = ztu->_common._link->_cap._flow == Z_LINK_CAP_FLOW_STREAM; is_reliable = ztu->_common._link->_cap._is_reliable; if (_z_socket_get_endpoints(&peer->_socket, local_addr, sizeof(local_addr), remote_addr, sizeof(remote_addr)) == _Z_RES_OK) { (void)_z_transport_make_endpoint(&ztu->_common._link->_endpoint._locator._protocol, local_addr, &peer->common._link_src); (void)_z_transport_make_endpoint(&ztu->_common._link->_endpoint._locator._protocol, remote_addr, &peer->common._link_dst); } } if (dispatch_connected_event) { _z_connectivity_peer_event_data_copy_from_common(&peer_event_data, &peer->common); } #endif _z_transport_peer_mutex_unlock(&ztu->_common); if (output_peer != NULL) { *output_peer = peer; } #if Z_FEATURE_CONNECTIVITY == 1 if (dispatch_connected_event) { _z_connectivity_peer_connected(session, &peer_event_data, false, mtu, is_streamed, is_reliable); } _z_connectivity_peer_event_data_clear(&peer_event_data); #endif return _Z_RES_OK; } ================================================ FILE: src/transport/raweth/link.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/link/config/raweth.h" #include "zenoh-pico/link/manager.h" #include "zenoh-pico/link/transport/raweth.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/system/platform.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #if Z_FEATURE_RAWETH_TRANSPORT == 1 #define RAWETH_CFG_TUPLE_SEPARATOR '#' #define RAWETH_CFG_LIST_SEPARATOR "," #define RAWETH_CONFIG_ARGC 4 #define RAWETH_CONFIG_IFACE_KEY 0x01 #define RAWETH_CONFIG_IFACE_STR "iface" #define RAWETH_CONFIG_ETHTYPE_KEY 0x02 #define RAWETH_CONFIG_ETHTYPE_STR "ethtype" #define RAWETH_CONFIG_MAPPING_KEY 0x03 #define RAWETH_CONFIG_MAPPING_STR "mapping" #define RAWETH_CONFIG_WHITELIST_KEY 0x04 #define RAWETH_CONFIG_WHITELIST_STR "whitelist" // Ethtype must be at least 0x600 in network order #define RAWETH_ETHTYPE_MIN_VALUE 0x600U #define RAWETH_CONFIG_MAPPING_BUILD \ _z_str_intmapping_t args[RAWETH_CONFIG_ARGC]; \ args[0]._key = RAWETH_CONFIG_IFACE_KEY; \ args[0]._str = RAWETH_CONFIG_IFACE_STR; \ args[1]._key = RAWETH_CONFIG_ETHTYPE_KEY; \ args[1]._str = RAWETH_CONFIG_ETHTYPE_STR; \ args[2]._key = RAWETH_CONFIG_MAPPING_KEY; \ args[2]._str = RAWETH_CONFIG_MAPPING_STR; \ args[3]._key = RAWETH_CONFIG_WHITELIST_KEY; \ args[3]._str = RAWETH_CONFIG_WHITELIST_STR; const uint16_t _ZP_RAWETH_DEFAULT_ETHTYPE = 0x72e0; const char *_ZP_RAWETH_DEFAULT_INTERFACE = "lo"; const uint8_t _ZP_RAWETH_DEFAULT_SMAC[_ZP_MAC_ADDR_LENGTH] = {0x30, 0x03, 0xc8, 0x37, 0x25, 0xa1}; const _zp_raweth_mapping_entry_t _ZP_RAWETH_DEFAULT_MAPPING = { ._keyexpr = {0}, ._vlan = 0x0000, ._dmac = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, ._has_vlan = false}; static bool _z_valid_iface_raweth(_z_str_intmap_t *config); static const char *_z_get_iface_raweth(_z_str_intmap_t *config); static bool _z_valid_ethtype_raweth(_z_str_intmap_t *config); static long _z_get_ethtype_raweth(_z_str_intmap_t *config); static size_t _z_valid_mapping_raweth(_z_str_intmap_t *config); static z_result_t _z_get_mapping_raweth(_z_str_intmap_t *config, _zp_raweth_mapping_array_t *array, size_t size); static size_t _z_valid_whitelist_raweth(_z_str_intmap_t *config); static z_result_t _z_get_whitelist_raweth(_z_str_intmap_t *config, _zp_raweth_whitelist_array_t *array, size_t size); static z_result_t _z_get_mapping_entry(char *entry, _zp_raweth_mapping_entry_t *storage); static bool _z_valid_mapping_entry(char *entry); static bool _z_valid_address_raweth_inner(const _z_string_t *address); static bool _z_valid_address_raweth(const char *address); static uint8_t *_z_parse_address_raweth(const char *address); static z_result_t _z_f_link_open_raweth(_z_link_t *self); static z_result_t _z_f_link_listen_raweth(_z_link_t *self); static void _z_f_link_close_raweth(_z_link_t *self); static void _z_f_link_free_raweth(_z_link_t *self); static size_t _z_f_link_write_raweth(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket); static size_t _z_f_link_write_all_raweth(const _z_link_t *self, const uint8_t *ptr, size_t len); static size_t _z_f_link_read_raweth(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr); static size_t _z_f_link_read_exact_raweth(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket); static uint16_t _z_get_link_mtu_raweth(void); static bool _z_valid_iface_raweth(_z_str_intmap_t *config) { const char *iface = _z_str_intmap_get(config, RAWETH_CONFIG_IFACE_KEY); return (iface != NULL); } static const char *_z_get_iface_raweth(_z_str_intmap_t *config) { return _z_str_intmap_get(config, RAWETH_CONFIG_IFACE_KEY); } static bool _z_valid_ethtype_raweth(_z_str_intmap_t *config) { const char *s_ethtype = _z_str_intmap_get(config, RAWETH_CONFIG_ETHTYPE_KEY); if (s_ethtype == NULL) { return false; } long ethtype = strtol(s_ethtype, NULL, 16); return (_z_raweth_htons((uint16_t)ethtype) > RAWETH_ETHTYPE_MIN_VALUE); } static long _z_get_ethtype_raweth(_z_str_intmap_t *config) { const char *s_ethtype = _z_str_intmap_get(config, RAWETH_CONFIG_ETHTYPE_KEY); return strtol(s_ethtype, NULL, 16); } static size_t _z_valid_mapping_raweth(_z_str_intmap_t *config) { // Retrieve list const char *cfg_str = _z_str_intmap_get(config, RAWETH_CONFIG_MAPPING_KEY); if (cfg_str == NULL) { return 0; } char *s_mapping = _z_str_clone(cfg_str); if (s_mapping == NULL) { return 0; } size_t size = 0; // Parse list const char *delim = RAWETH_CFG_LIST_SEPARATOR; char *entry = strtok(s_mapping, delim); while (entry != NULL) { // Check entry if (!_z_valid_mapping_entry(entry)) { z_free(s_mapping); return 0; } size++; entry = strtok(NULL, delim); } // Clean up z_free(s_mapping); return size; } static z_result_t _z_get_mapping_raweth(_z_str_intmap_t *config, _zp_raweth_mapping_array_t *array, size_t size) { // Retrieve data const char *cfg_str = _z_str_intmap_get(config, RAWETH_CONFIG_MAPPING_KEY); if (cfg_str == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Copy data char *s_mapping = _z_str_clone(cfg_str); if (s_mapping == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Allocate array *array = _zp_raweth_mapping_array_make(size); if (_zp_raweth_mapping_array_len(array) == 0) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } size_t idx = 0; // Parse list const char *delim = RAWETH_CFG_LIST_SEPARATOR; char *entry = strtok(s_mapping, delim); while ((entry != NULL) && (idx < _zp_raweth_mapping_array_len(array))) { // Copy data into array _Z_CLEAN_RETURN_IF_ERR(_z_get_mapping_entry(entry, _zp_raweth_mapping_array_get(array, idx)), z_free(s_mapping)); // Next iteration idx++; entry = strtok(NULL, delim); } // Clean up z_free(s_mapping); return _Z_RES_OK; } static size_t _z_valid_whitelist_raweth(_z_str_intmap_t *config) { // Retrieve data const char *cfg_str = _z_str_intmap_get(config, RAWETH_CONFIG_WHITELIST_KEY); if (cfg_str == NULL) { return 0; } // Copy data char *s_whitelist = _z_str_clone(cfg_str); if (s_whitelist == NULL) { return 0; } // Parse list size_t size = 0; const char *delim = RAWETH_CFG_LIST_SEPARATOR; char *entry = strtok(s_whitelist, delim); while (entry != NULL) { // Check entry if (!_z_valid_address_raweth(entry)) { z_free(s_whitelist); return 0; } size++; entry = strtok(NULL, delim); } // Parse last entry // Clean up z_free(s_whitelist); return size; } static z_result_t _z_get_whitelist_raweth(_z_str_intmap_t *config, _zp_raweth_whitelist_array_t *array, size_t size) { // Retrieve data const char *cfg_str = _z_str_intmap_get(config, RAWETH_CONFIG_WHITELIST_KEY); if (cfg_str == NULL) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } // Copy data char *s_whitelist = _z_str_clone(cfg_str); if (s_whitelist == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Allocate array *array = _zp_raweth_whitelist_array_make(size); if (_zp_raweth_whitelist_array_len(array) == 0) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } size_t idx = 0; // Parse list const char *delim = RAWETH_CFG_LIST_SEPARATOR; char *entry = strtok(s_whitelist, delim); while ((entry != NULL) && (idx < _zp_raweth_whitelist_array_len(array))) { // Convert address from string to int array uint8_t *addr = _z_parse_address_raweth(entry); if (addr == NULL) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Copy address to entry _zp_raweth_whitelist_entry_t *elem = _zp_raweth_whitelist_array_get(array, idx); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(elem->_mac, addr, _ZP_MAC_ADDR_LENGTH); z_free(addr); // Next iteration idx++; entry = strtok(NULL, delim); } // Clean up z_free(s_whitelist); return _Z_RES_OK; } static z_result_t _z_get_mapping_entry(char *entry, _zp_raweth_mapping_entry_t *storage) { // Flawfinder: ignore [CWE-126] - entry is a '\0'-terminated token produced from a cloned config string. size_t len = strlen(entry); const char *entry_end = &entry[len - (size_t)1]; // Get first tuple member (keyexpr) char *p_start = &entry[0]; char *p_end = strchr(p_start, RAWETH_CFG_TUPLE_SEPARATOR); size_t ke_len = (uintptr_t)p_end - (uintptr_t)p_start; storage->_keyexpr = _z_string_copy_from_substr(p_start, ke_len); if (!_z_string_check(&storage->_keyexpr)) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } // Check second entry (address) p_start = p_end; p_start++; p_end = strchr(p_start, RAWETH_CFG_TUPLE_SEPARATOR); *p_end = '\0'; uint8_t *addr = _z_parse_address_raweth(p_start); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(storage->_dmac, addr, _ZP_MAC_ADDR_LENGTH); z_free(addr); *p_end = RAWETH_CFG_TUPLE_SEPARATOR; // Check optional third entry (vlan id) p_start = p_end; p_start++; if (p_start >= entry_end) { // No entry storage->_has_vlan = false; } else { storage->_has_vlan = true; storage->_vlan = (uint16_t)strtol(p_start, NULL, 16); } return _Z_RES_OK; } static bool _z_valid_mapping_entry(char *entry) { // Flawfinder: ignore [CWE-126] - entry is a '\0'-terminated token produced from a cloned config string. size_t len = strlen(entry); const char *entry_end = &entry[len - (size_t)1]; // Check first tuple member (keyexpr) char *p_start = &entry[0]; char *p_end = strchr(p_start, RAWETH_CFG_TUPLE_SEPARATOR); if (p_end == NULL) { return false; } // Check second entry (address) p_start = p_end; p_start++; if (p_start > entry_end) { return false; } p_end = strchr(p_start, RAWETH_CFG_TUPLE_SEPARATOR); if (p_end == NULL) { return false; } *p_end = '\0'; if (!_z_valid_address_raweth(p_start)) { *p_end = RAWETH_CFG_TUPLE_SEPARATOR; return false; } *p_end = RAWETH_CFG_TUPLE_SEPARATOR; return true; } static bool _z_valid_address_raweth_inner(const _z_string_t *address) { // Check if the string has the correct length size_t len = _z_string_len(address); const char *str_data = _z_string_data(address); if (len != 17) { // 6 pairs of hexadecimal digits and 5 colons return false; } // Check if the colons are at the correct positions for (size_t i = 2; i < len; i += 3) { if (str_data[i] != ':') { return false; } } // Check if each character is a valid hexadecimal digit for (size_t i = 0; i < len; ++i) { if (i % 3 != 2) { char c = str_data[i]; if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) { return false; } } } return true; } static bool _z_valid_address_raweth(const char *address) { _z_string_t addr_str = _z_string_alias_str(address); return _z_valid_address_raweth_inner(&addr_str); } static uint8_t *_z_parse_address_raweth(const char *address) { // Allocate data uint8_t *ret = (uint8_t *)z_malloc(_ZP_MAC_ADDR_LENGTH); if (ret == NULL) { return ret; } for (size_t i = 0; i < _ZP_MAC_ADDR_LENGTH; ++i) { // Flawfinder: ignore [CWE-120] - fixed 2-digit hex byte plus explicit terminator. char byte_string[3] = {address[i * 3], address[(i * 3) + 1], '\0'}; ret[i] = (uint8_t)strtol(byte_string, NULL, 16); } return ret; } static z_result_t _z_f_link_open_raweth(_z_link_t *self) { // Init arrays self->_socket._raweth._mapping = _zp_raweth_mapping_array_empty(); self->_socket._raweth._whitelist = _zp_raweth_whitelist_array_empty(); // Init socket smac if (_z_valid_address_raweth_inner(&self->_endpoint._locator._address)) { uint8_t *addr = _z_parse_address_raweth(_z_string_data(&self->_endpoint._locator._address)); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(self->_socket._raweth._smac, addr, _ZP_MAC_ADDR_LENGTH); z_free(addr); } else { _Z_DEBUG("Invalid locator source mac addr, using default value."); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(self->_socket._raweth._smac, _ZP_RAWETH_DEFAULT_SMAC, _ZP_MAC_ADDR_LENGTH); } // Init socket interface if (_z_valid_iface_raweth(&self->_endpoint._config)) { self->_socket._raweth._interface = _z_get_iface_raweth(&self->_endpoint._config); } else { _Z_DEBUG("Invalid locator interface, using default value %s", _ZP_RAWETH_DEFAULT_INTERFACE); self->_socket._raweth._interface = _ZP_RAWETH_DEFAULT_INTERFACE; } // Init socket ethtype if (_z_valid_ethtype_raweth(&self->_endpoint._config)) { self->_socket._raweth._ethtype = (uint16_t)_z_get_ethtype_raweth(&self->_endpoint._config); } else { _Z_DEBUG("Invalid locator ethtype, using default value 0x%04x", _ZP_RAWETH_DEFAULT_ETHTYPE); self->_socket._raweth._ethtype = _ZP_RAWETH_DEFAULT_ETHTYPE; } // Init socket mapping size_t size = _z_valid_mapping_raweth(&self->_endpoint._config); if (size != (size_t)0) { _Z_RETURN_IF_ERR(_z_get_mapping_raweth(&self->_endpoint._config, &self->_socket._raweth._mapping, size)); } else { _Z_DEBUG("Invalid locator mapping, using default value."); self->_socket._raweth._mapping = _zp_raweth_mapping_array_make(1); if (_zp_raweth_mapping_array_len(&self->_socket._raweth._mapping) == 0) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _zp_raweth_mapping_entry_t *entry = _zp_raweth_mapping_array_get(&self->_socket._raweth._mapping, 0); *entry = _ZP_RAWETH_DEFAULT_MAPPING; } // Init socket whitelist size = _z_valid_whitelist_raweth(&self->_endpoint._config); if (size != (size_t)0) { _Z_RETURN_IF_ERR(_z_get_whitelist_raweth(&self->_endpoint._config, &self->_socket._raweth._whitelist, size)); } else { _Z_DEBUG("Invalid locator whitelist, filtering deactivated."); } // Open raweth link return _z_open_raweth(&self->_socket._raweth._sock, self->_socket._raweth._interface); } static z_result_t _z_f_link_listen_raweth(_z_link_t *self) { return _z_f_link_open_raweth(self); } static void _z_f_link_close_raweth(_z_link_t *self) { // Close connection _z_close_raweth(&self->_socket._raweth._sock); // Clear config _zp_raweth_mapping_array_clear(&self->_socket._raweth._mapping); if (_zp_raweth_whitelist_array_len(&self->_socket._raweth._whitelist) != 0) { _zp_raweth_whitelist_array_clear(&self->_socket._raweth._whitelist); } } static void _z_f_link_free_raweth(_z_link_t *self) { _ZP_UNUSED(self); } static size_t _z_f_link_write_raweth(const _z_link_t *self, const uint8_t *ptr, size_t len, _z_sys_net_socket_t *socket) { _ZP_UNUSED(self); _ZP_UNUSED(ptr); _ZP_UNUSED(len); _ZP_UNUSED(socket); return SIZE_MAX; } static size_t _z_f_link_write_all_raweth(const _z_link_t *self, const uint8_t *ptr, size_t len) { _ZP_UNUSED(self); _ZP_UNUSED(ptr); _ZP_UNUSED(len); return SIZE_MAX; } static size_t _z_f_link_read_raweth(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr) { _ZP_UNUSED(self); _ZP_UNUSED(ptr); _ZP_UNUSED(len); _ZP_UNUSED(addr); return SIZE_MAX; } static size_t _z_f_link_read_exact_raweth(const _z_link_t *self, uint8_t *ptr, size_t len, _z_slice_t *addr, _z_sys_net_socket_t *socket) { _ZP_UNUSED(self); _ZP_UNUSED(ptr); _ZP_UNUSED(len); _ZP_UNUSED(addr); _ZP_UNUSED(socket); return SIZE_MAX; } static uint16_t _z_get_link_mtu_raweth(void) { return _ZP_MAX_ETH_FRAME_SIZE; } z_result_t _z_endpoint_raweth_valid(_z_endpoint_t *endpoint) { z_result_t ret = _Z_RES_OK; // Check the root _z_string_t str_cmp = _z_string_alias_str(RAWETH_SCHEMA); if (!_z_string_equals(&endpoint->_locator._protocol, &str_cmp)) { _Z_ERROR_LOG(_Z_ERR_CONFIG_LOCATOR_INVALID); ret = _Z_ERR_CONFIG_LOCATOR_INVALID; } return ret; } z_result_t _z_new_link_raweth(_z_link_t *zl, _z_endpoint_t endpoint) { z_result_t ret = _Z_RES_OK; zl->_type = _Z_LINK_TYPE_RAWETH; zl->_cap._transport = Z_LINK_CAP_TRANSPORT_RAWETH; zl->_cap._is_reliable = false; zl->_mtu = _z_get_link_mtu_raweth(); zl->_endpoint = endpoint; // Init socket memset(&zl->_socket._raweth, 0, sizeof(zl->_socket._raweth)); zl->_open_f = _z_f_link_open_raweth; zl->_listen_f = _z_f_link_listen_raweth; zl->_close_f = _z_f_link_close_raweth; zl->_free_f = _z_f_link_free_raweth; zl->_write_f = _z_f_link_write_raweth; zl->_write_all_f = _z_f_link_write_all_raweth; zl->_read_f = _z_f_link_read_raweth; zl->_read_exact_f = _z_f_link_read_exact_raweth; zl->_read_socket_f = _z_noop_link_read_socket; return ret; } size_t _z_raweth_config_strlen(const _z_str_intmap_t *s) { RAWETH_CONFIG_MAPPING_BUILD return _z_str_intmap_strlen(s, RAWETH_CONFIG_ARGC, args); } char *_z_raweth_config_to_str(const _z_str_intmap_t *s) { RAWETH_CONFIG_MAPPING_BUILD return _z_str_intmap_to_str(s, RAWETH_CONFIG_ARGC, args); } z_result_t _z_raweth_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { RAWETH_CONFIG_MAPPING_BUILD return _z_str_intmap_from_strn(strint, s, RAWETH_CONFIG_ARGC, args, n); } z_result_t _z_raweth_config_from_str(_z_str_intmap_t *strint, const char *s) { // Flawfinder: ignore [CWE-126] - public from_str() expects a conventional '\0'-terminated C string. return _z_raweth_config_from_strn(strint, s, strlen(s)); } #else z_result_t _z_endpoint_raweth_valid(_z_endpoint_t *endpoint) { _ZP_UNUSED(endpoint); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_new_link_raweth(_z_link_t *zl, _z_endpoint_t endpoint) { _ZP_UNUSED(zl); _ZP_UNUSED(endpoint); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } size_t _z_raweth_config_strlen(const _z_str_intmap_t *s) { _ZP_UNUSED(s); return 0; } char *_z_raweth_config_to_str(const _z_str_intmap_t *s) { _ZP_UNUSED(s); return NULL; } z_result_t _z_raweth_config_from_strn(_z_str_intmap_t *strint, const char *s, size_t n) { _ZP_UNUSED(strint); _ZP_UNUSED(s); _ZP_UNUSED(n); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_raweth_config_from_str(_z_str_intmap_t *strint, const char *s) { _ZP_UNUSED(strint); _ZP_UNUSED(s); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif ================================================ FILE: src/transport/raweth/read.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/raweth/read.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/raweth/rx.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_RAWETH_TRANSPORT == 1 z_result_t _zp_raweth_read(_z_transport_multicast_t *ztm, bool single_read) { z_result_t ret = _Z_RES_OK; _ZP_UNUSED(single_read); _z_slice_t addr; _z_transport_message_t t_msg; ret = _z_raweth_recv_t_msg(ztm, &t_msg, &addr); if (ret == _Z_RES_OK) { ret = _z_multicast_handle_transport_message(ztm, &t_msg, &addr); _z_t_msg_clear(&t_msg); } _z_slice_clear(&addr); ret = _z_raweth_update_rx_buff(ztm); if (ret != _Z_RES_OK) { _Z_ERROR("Failed to allocate rx buffer"); } return ret; } _z_fut_fn_result_t _zp_raweth_read_task_fn(void *ztm_arg, _z_executor_t *executor) { _ZP_UNUSED(executor); _z_transport_multicast_t *ztm = (_z_transport_multicast_t *)ztm_arg; if (ztm->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztm->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } _z_transport_message_t t_msg; _z_slice_t addr = _z_slice_alias_buf(NULL, 0); // Read message from link z_result_t ret = _z_raweth_recv_t_msg(ztm, &t_msg, &addr); switch (ret) { case _Z_RES_OK: // Process message break; case _Z_ERR_TRANSPORT_RX_FAILED: // Drop message _z_slice_clear(&addr); return _z_fut_fn_result_continue(); default: // Drop message & stop task _Z_ERROR("Connection closed due to malformed message: %d", ret); _z_slice_clear(&addr); return _z_fut_fn_result_ready(); } // Process message ret = _z_multicast_handle_transport_message(ztm, &t_msg, &addr); if (ret != _Z_RES_OK) { _Z_ERROR("Connection closed due to message processing error: %d", ret); _z_slice_clear(&addr); return _z_fut_fn_result_ready(); } _z_t_msg_clear(&t_msg); _z_slice_clear(&addr); if (_z_raweth_update_rx_buff(ztm) != _Z_RES_OK) { _Z_ERROR("Connection closed due to lack of memory to allocate rx buffer"); return _z_fut_fn_result_ready(); } return _z_fut_fn_result_continue(); } #endif ================================================ FILE: src/transport/raweth/rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // #include "zenoh-pico/transport/link/rx.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_RAWETH_TRANSPORT == 1 static size_t _z_raweth_link_recv_zbuf(const _z_link_t *link, _z_zbuf_t *zbf, _z_slice_t *addr) { uint8_t *buff = _z_zbuf_get_wptr(zbf); size_t rb = _z_receive_raweth(&link->_socket._raweth._sock, buff, _z_zbuf_space_left(zbf), addr, &link->_socket._raweth._whitelist); // Check validity if ((rb == SIZE_MAX) || (rb < sizeof(_zp_eth_header_t))) { return SIZE_MAX; } // Check if header has vlan bool has_vlan = false; _zp_eth_header_t *header = (_zp_eth_header_t *)buff; if (header->ethtype == _ZP_ETH_TYPE_VLAN) { has_vlan = true; } // Check validity if (has_vlan && (rb < sizeof(_zp_eth_vlan_header_t))) { return SIZE_MAX; } size_t data_length = 0; if (has_vlan) { _zp_eth_vlan_header_t *vlan_header = (_zp_eth_vlan_header_t *)buff; // Retrieve data length data_length = _z_raweth_ntohs(vlan_header->data_length); if (rb < (data_length + sizeof(_zp_eth_vlan_header_t))) { // Invalid data_length return SIZE_MAX; } // Skip header _z_zbuf_set_wpos(zbf, _z_zbuf_get_wpos(zbf) + sizeof(_zp_eth_vlan_header_t) + data_length); _z_zbuf_set_rpos(zbf, _z_zbuf_get_rpos(zbf) + sizeof(_zp_eth_vlan_header_t)); } else { header = (_zp_eth_header_t *)buff; // Retrieve data length data_length = _z_raweth_ntohs(header->data_length); if (rb < (data_length + sizeof(_zp_eth_header_t))) { // Invalid data_length return SIZE_MAX; } // Skip header _z_zbuf_set_wpos(zbf, _z_zbuf_get_wpos(zbf) + sizeof(_zp_eth_header_t) + data_length); _z_zbuf_set_rpos(zbf, _z_zbuf_get_rpos(zbf) + sizeof(_zp_eth_header_t)); } return data_length; } /*------------------ Reception helper ------------------*/ z_result_t _z_raweth_recv_t_msg_na(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr) { _Z_DEBUG(">> recv session msg"); z_result_t ret = _Z_RES_OK; // Prepare the buffer _z_zbuf_reset(&ztm->_common._zbuf); switch (ztm->_common._link->_cap._flow) { // Datagram capable links case Z_LINK_CAP_FLOW_DATAGRAM: { _z_zbuf_compact(&ztm->_common._zbuf); // Read from link size_t to_read = _z_raweth_link_recv_zbuf(ztm->_common._link, &ztm->_common._zbuf, addr); if (to_read == SIZE_MAX) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_RX_FAILED); ret = _Z_ERR_TRANSPORT_RX_FAILED; } break; } default: _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; break; } // Decode message if (ret == _Z_RES_OK) { _Z_DEBUG(">> \t transport_message_decode: %ju", (uintmax_t)_z_zbuf_len(&ztm->_common._zbuf)); ret = _z_transport_message_decode(t_msg, &ztm->_common._zbuf); } return ret; } z_result_t _z_raweth_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr) { return _z_raweth_recv_t_msg_na(ztm, t_msg, addr); } z_result_t _z_raweth_update_rx_buff(_z_transport_multicast_t *ztm) { // Check if user or defragment buffer took ownership of buffer if (_z_zbuf_get_ref_count(&ztm->_common._zbuf) != 1) { // Allocate a new buffer _z_zbuf_t new_zbuf = _z_zbuf_make(Z_BATCH_MULTICAST_SIZE); if (_z_zbuf_capacity(&new_zbuf) != Z_BATCH_MULTICAST_SIZE) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Recopy leftover bytes size_t leftovers = _z_zbuf_len(&ztm->_common._zbuf); if (leftovers > 0) { _z_zbuf_copy_bytes(&new_zbuf, &ztm->_common._zbuf); } // Drop buffer & update _z_zbuf_clear(&ztm->_common._zbuf); ztm->_common._zbuf = new_zbuf; } return _Z_RES_OK; } #else z_result_t _z_raweth_recv_t_msg(_z_transport_multicast_t *ztm, _z_transport_message_t *t_msg, _z_slice_t *addr) { _ZP_UNUSED(ztm); _ZP_UNUSED(t_msg); _ZP_UNUSED(addr); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif // Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/transport/raweth/tx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // #include "zenoh-pico/transport/link/tx.h" #include "zenoh-pico/transport/common/tx.h" #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/transport/raweth.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/transport/multicast/transport.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_RAWETH_TRANSPORT == 1 static int _zp_raweth_find_map_entry(const _z_keyexpr_t *keyexpr, _z_raweth_socket_t *sock) { for (size_t i = 0; i < _zp_raweth_mapping_array_len(&sock->_mapping); i++) { // Find matching keyexpr const _zp_raweth_mapping_entry_t *entry = _zp_raweth_mapping_array_get(&sock->_mapping, i); _z_keyexpr_t entry_ke = _z_keyexpr_alias_from_string(&entry->_keyexpr); if (!z_keyexpr_intersects(keyexpr, &entry_ke)) { continue; } return (int)i; } return -1; } static z_result_t _zp_raweth_set_socket(const _z_keyexpr_t *keyexpr, _z_raweth_socket_t *sock) { z_result_t ret = _Z_RES_OK; if (_zp_raweth_mapping_array_len(&sock->_mapping) < 1) { _Z_ERROR_RETURN(_Z_ERR_GENERIC); } if (keyexpr == NULL) { // Store default value into socket const _zp_raweth_mapping_entry_t *entry = _zp_raweth_mapping_array_get(&sock->_mapping, 0); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(sock->_dmac, entry->_dmac, _ZP_MAC_ADDR_LENGTH); sock->_has_vlan = entry->_has_vlan; if (sock->_has_vlan) { sock->_vlan = entry->_vlan; } } else { // Find config entry (linear) int idx = _zp_raweth_find_map_entry(keyexpr, sock); // Key not found case if (idx < 0) { idx = 0; // Set to default entry _Z_DEBUG("Key '%.*s' wasn't found in config mapping, sending to default address", (int)_z_string_len(&keyexpr), _z_string_data(&keyexpr)); } // Store data into socket const _zp_raweth_mapping_entry_t *entry = _zp_raweth_mapping_array_get(&sock->_mapping, (size_t)idx); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(sock->_dmac, entry->_dmac, _ZP_MAC_ADDR_LENGTH); sock->_has_vlan = entry->_has_vlan; if (sock->_has_vlan) { sock->_vlan = entry->_vlan; } } return ret; } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - ztm->_mutex_inner */ static _z_zint_t __unsafe_z_raweth_get_sn(_z_transport_multicast_t *ztm, z_reliability_t reliability) { _z_zint_t sn; if (reliability == Z_RELIABILITY_RELIABLE) { sn = ztm->_common._sn_tx_reliable; ztm->_common._sn_tx_reliable = _z_sn_increment(ztm->_common._sn_res, ztm->_common._sn_tx_reliable); } else { sn = ztm->_common._sn_tx_best_effort; ztm->_common._sn_tx_best_effort = _z_sn_increment(ztm->_common._sn_res, ztm->_common._sn_tx_best_effort); } return sn; } static void __unsafe_z_raweth_prepare_header(_z_link_t *zl, _z_wbuf_t *wbf) { _z_raweth_socket_t *resocket = &zl->_socket._raweth; // Reserve eth header in buffer if (resocket->_has_vlan) { _z_wbuf_set_wpos(wbf, sizeof(_zp_eth_vlan_header_t)); } else { _z_wbuf_set_wpos(wbf, sizeof(_zp_eth_header_t)); } } /** * This function is unsafe because it operates in potentially concurrent data. * Make sure that the following mutexes are locked before calling this function: * - ztm->_mutex_inner */ static z_result_t __unsafe_z_raweth_write_header(_z_link_t *zl, _z_wbuf_t *wbf) { _z_raweth_socket_t *resocket = &zl->_socket._raweth; // Save and reset buffer position size_t wpos = _z_wbuf_len(wbf); _z_wbuf_set_wpos(wbf, 0); // Write eth header in buffer if (resocket->_has_vlan) { _zp_eth_vlan_header_t header; // Set header memset(&header, 0, sizeof(header)); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(header.dmac, resocket->_dmac, _ZP_MAC_ADDR_LENGTH); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(header.smac, resocket->_smac, _ZP_MAC_ADDR_LENGTH); header.vlan_type = _ZP_ETH_TYPE_VLAN; header.tag = resocket->_vlan; header.ethtype = resocket->_ethtype; header.data_length = _z_raweth_htons((uint16_t)(wpos - sizeof(header))); // Write header _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, (uint8_t *)&header, 0, sizeof(header))); } else { _zp_eth_header_t header; // Set header // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(header.dmac, resocket->_dmac, _ZP_MAC_ADDR_LENGTH); // Flawfinder: ignore [CWE-120] - fixed-size MAC copy, both operands are _ZP_MAC_ADDR_LENGTH bytes. memcpy(header.smac, resocket->_smac, _ZP_MAC_ADDR_LENGTH); header.ethtype = resocket->_ethtype; header.data_length = _z_raweth_htons((uint16_t)(wpos - sizeof(header))); // Write header _Z_RETURN_IF_ERR(_z_wbuf_write_bytes(wbf, (uint8_t *)&header, 0, sizeof(header))); } // Restore wpos _z_wbuf_set_wpos(wbf, wpos); return _Z_RES_OK; } static z_result_t _z_raweth_link_send_wbuf(const _z_link_t *zl, const _z_wbuf_t *wbf) { z_result_t ret = _Z_RES_OK; for (size_t i = 0; (i < _z_wbuf_len_iosli(wbf)) && (ret == _Z_RES_OK); i++) { _z_slice_t bs = _z_iosli_to_bytes(_z_wbuf_get_iosli(wbf, i)); size_t n = bs.len; do { // Retrieve addr from config + vlan tag above (locator) size_t wb = _z_send_raweth(&zl->_socket._raweth._sock, bs.start, n); // Unix if (wb == SIZE_MAX) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_TX_FAILED); } n = n - wb; bs.start = bs.start + (bs.len - n); } while (n > (size_t)0); } return ret; } z_result_t _z_raweth_link_send_t_msg(const _z_link_t *zl, const _z_transport_message_t *t_msg) { z_result_t ret = _Z_RES_OK; // Create and prepare the buffer to serialize the message on uint16_t mtu = (zl->_mtu < Z_BATCH_UNICAST_SIZE) ? zl->_mtu : Z_BATCH_UNICAST_SIZE; _z_wbuf_t wbf = _z_wbuf_make(mtu, false); if (_z_wbuf_capacity(&wbf) != mtu) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } // Discard const qualifier _z_link_t *mzl = (_z_link_t *)zl; // Set socket info _Z_RETURN_IF_ERR(_zp_raweth_set_socket(NULL, &mzl->_socket._raweth)); // Prepare buff __unsafe_z_raweth_prepare_header(mzl, &wbf); // Encode the session message _Z_RETURN_IF_ERR(_z_transport_message_encode(&wbf, t_msg)); // Write the message header _Z_RETURN_IF_ERR(__unsafe_z_raweth_write_header(mzl, &wbf)); // Send the wbuf on the socket ret = _z_raweth_link_send_wbuf(zl, &wbf); _z_wbuf_clear(&wbf); return ret; } z_result_t _z_raweth_send_t_msg(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg) { z_result_t ret = _Z_RES_OK; _Z_DEBUG(">> send session message"); _z_transport_tx_mutex_lock(ztc, true); // Reset wbuf _z_wbuf_reset(&ztc->_wbuf); // Set socket info _Z_CLEAN_RETURN_IF_ERR(_zp_raweth_set_socket(NULL, &ztc->_link->_socket._raweth), _z_transport_tx_mutex_unlock(ztc)); // Prepare buff __unsafe_z_raweth_prepare_header(ztc->_link, &ztc->_wbuf); // Encode the session message _Z_CLEAN_RETURN_IF_ERR(_z_transport_message_encode(&ztc->_wbuf, t_msg), _z_transport_tx_mutex_unlock(ztc)); // Write the message header _Z_CLEAN_RETURN_IF_ERR(__unsafe_z_raweth_write_header(ztc->_link, &ztc->_wbuf), _z_transport_tx_mutex_unlock(ztc)); // Send the wbuf on the socket _Z_CLEAN_RETURN_IF_ERR(_z_raweth_link_send_wbuf(ztc->_link, &ztc->_wbuf), _z_transport_tx_mutex_unlock(ztc)); // Mark the session that we have transmitted data ztc->_transmitted = true; _z_transport_tx_mutex_unlock(ztc); return ret; } z_result_t _z_raweth_send_n_msg(_z_session_t *zn, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl) { z_result_t ret = _Z_RES_OK; _z_transport_multicast_t *ztm = &zn->_tp._transport._raweth; _Z_DEBUG(">> send network message"); // Acquire the lock and drop the message if needed ret = _z_transport_tx_mutex_lock(&ztm->_common, cong_ctrl == Z_CONGESTION_CONTROL_BLOCK); if (ret != _Z_RES_OK) { _Z_INFO("Dropping zenoh message because of congestion control"); return ret; } const _z_keyexpr_t *keyexpr = NULL; switch (n_msg->_tag) { case _Z_N_PUSH: keyexpr = &n_msg->_body._push._key; break; case _Z_N_REQUEST: keyexpr = &n_msg->_body._request._key; break; case _Z_N_RESPONSE: keyexpr = &n_msg->_body._response._key; break; case _Z_N_RESPONSE_FINAL: case _Z_N_DECLARE: default: break; } // Reset wbuf _z_wbuf_reset(&ztm->_common._wbuf); // Set socket info _Z_CLEAN_RETURN_IF_ERR(_zp_raweth_set_socket(keyexpr, &ztm->_common._link->_socket._raweth), _z_transport_tx_mutex_unlock(&ztm->_common)); // Prepare buff __unsafe_z_raweth_prepare_header(ztm->_common._link, &ztm->_common._wbuf); // Set the frame header _z_zint_t sn = __unsafe_z_raweth_get_sn(ztm, reliability); _z_transport_message_t t_msg = _z_t_msg_make_frame_header(sn, reliability); // Encode the frame header _Z_CLEAN_RETURN_IF_ERR(_z_transport_message_encode(&ztm->_common._wbuf, &t_msg), _z_transport_tx_mutex_unlock(&ztm->_common)); // Encode the network message if (_z_network_message_encode(&ztm->_common._wbuf, n_msg) == _Z_RES_OK) { // Write the eth header _Z_CLEAN_RETURN_IF_ERR(__unsafe_z_raweth_write_header(ztm->_common._link, &ztm->_common._wbuf), _z_transport_tx_mutex_unlock(&ztm->_common)); // Send the wbuf on the socket _Z_CLEAN_RETURN_IF_ERR(_z_raweth_link_send_wbuf(ztm->_common._link, &ztm->_common._wbuf), _z_transport_tx_mutex_unlock(&ztm->_common)); // Mark the session that we have transmitted data ztm->_common._transmitted = true; } else { // The message does not fit in the current batch, let's fragment it #if Z_FEATURE_FRAGMENTATION == 1 // Create an expandable wbuf for fragmentation _z_wbuf_t fbf = _z_wbuf_make(_Z_FRAG_BUFF_BASE_SIZE, true); if (_z_wbuf_capacity(&fbf) != _Z_FRAG_BUFF_BASE_SIZE) { _Z_ERROR_LOG(_Z_ERR_SYSTEM_OUT_OF_MEMORY); _z_transport_tx_mutex_unlock(&ztm->_common); return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } // Encode the message on the expandable wbuf _Z_CLEAN_RETURN_IF_ERR(_z_network_message_encode(&fbf, n_msg), _z_transport_tx_mutex_unlock(&ztm->_common)); // Fragment and send the message bool is_first = true; while (_z_wbuf_len(&fbf) > 0) { if (is_first) { // Get the fragment sequence number sn = __unsafe_z_raweth_get_sn(ztm, reliability); } // Reset wbuf _z_wbuf_reset(&ztm->_common._wbuf); // Prepare buff __unsafe_z_raweth_prepare_header(ztm->_common._link, &ztm->_common._wbuf); // Serialize one fragment _Z_CLEAN_RETURN_IF_ERR( __unsafe_z_serialize_zenoh_fragment(&ztm->_common._wbuf, &fbf, reliability, sn, is_first), _z_transport_tx_mutex_unlock(&ztm->_common)); // Write the eth header _Z_CLEAN_RETURN_IF_ERR(__unsafe_z_raweth_write_header(ztm->_common._link, &ztm->_common._wbuf), _z_transport_tx_mutex_unlock(&ztm->_common)); // Send the wbuf on the socket _Z_CLEAN_RETURN_IF_ERR(_z_raweth_link_send_wbuf(ztm->_common._link, &ztm->_common._wbuf), _z_transport_tx_mutex_unlock(&ztm->_common)); // Mark the session that we have transmitted data ztm->_common._transmitted = true; is_first = false; } // Clear the expandable buffer _z_wbuf_clear(&fbf); #else _Z_INFO("Sending the message required fragmentation feature that is deactivated."); #endif } _z_transport_tx_mutex_unlock(&ztm->_common); return ret; } #else z_result_t _z_raweth_link_send_t_msg(const _z_link_t *zl, const _z_transport_message_t *t_msg) { _ZP_UNUSED(zl); _ZP_UNUSED(t_msg); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_raweth_send_t_msg(_z_transport_common_t *ztc, const _z_transport_message_t *t_msg) { _ZP_UNUSED(ztc); _ZP_UNUSED(t_msg); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_raweth_send_n_msg(_z_session_t *zn, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl) { _ZP_UNUSED(zn); _ZP_UNUSED(n_msg); _ZP_UNUSED(reliability); _ZP_UNUSED(cong_ctrl); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif // Z_FEATURE_RAWETH_TRANSPORT == 1 ================================================ FILE: src/transport/transport.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/multicast/transport.h" #include #include #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/sleep.h" size_t _z_transport_get_peers_count(_z_transport_t *zt) { size_t count = 0; switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: _z_transport_peer_mutex_lock(&zt->_transport._unicast._common); count = _z_transport_peer_unicast_slist_len(zt->_transport._unicast._peers); _z_transport_peer_mutex_unlock(&zt->_transport._unicast._common); return count; case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: _z_transport_peer_mutex_lock(&zt->_transport._multicast._common); count = _z_transport_peer_multicast_slist_len(zt->_transport._multicast._peers); _z_transport_peer_mutex_unlock(&zt->_transport._multicast._common); return count; default: return 0; } } _z_transport_common_t *_z_transport_get_common(_z_transport_t *zt) { switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: return &zt->_transport._unicast._common; case _Z_TRANSPORT_MULTICAST_TYPE: return &zt->_transport._multicast._common; case _Z_TRANSPORT_RAWETH_TYPE: return &zt->_transport._raweth._common; default: _Z_DEBUG("None transport, it should never happens"); return NULL; } } z_result_t _z_send_close(_z_transport_t *zt, uint8_t reason, bool link_only) { z_result_t ret = _Z_RES_OK; // Call transport function switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: ret = _z_unicast_send_close(&zt->_transport._unicast, reason, link_only); break; case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: ret = _z_multicast_send_close(&zt->_transport._multicast, reason, link_only); break; default: _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_AVAILABLE); ret = _Z_ERR_TRANSPORT_NOT_AVAILABLE; break; } return ret; } z_result_t _z_transport_close(_z_transport_t *zt, uint8_t reason) { return _z_send_close(zt, reason, false); } void _z_transport_clear(_z_transport_t *zt) { switch (zt->_type) { case _Z_TRANSPORT_UNICAST_TYPE: _z_unicast_transport_clear(&zt->_transport._unicast); break; case _Z_TRANSPORT_MULTICAST_TYPE: case _Z_TRANSPORT_RAWETH_TYPE: _z_multicast_transport_clear(&zt->_transport._multicast); break; default: return; } _z_transport_get_common(zt)->_state = _Z_TRANSPORT_STATE_CLOSED; zt->_type = _Z_TRANSPORT_NONE; } void _z_transport_free(_z_transport_t **zt) { _z_transport_t *ptr = *zt; if (ptr == NULL) { return; } // Clear and free transport _z_transport_clear(ptr); z_free(ptr); *zt = NULL; } #if Z_FEATURE_BATCHING == 1 z_result_t _z_transport_start_batching(_z_transport_t *zt) { _z_transport_common_t *ztc = _z_transport_get_common(zt); if (ztc == NULL) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } if (ztc->_batch_state == _Z_BATCHING_ACTIVE) { return _Z_ERR_GENERIC; } ztc->_batch_count = 0; ztc->_batch_state = _Z_BATCHING_ACTIVE; #if Z_FEATURE_BATCH_TX_MUTEX == 1 _z_transport_tx_mutex_lock(ztc, true); #endif #if Z_FEATURE_BATCH_PEER_MUTEX == 1 _z_transport_peer_mutex_lock(ztc); #endif return _Z_RES_OK; } z_result_t _z_transport_stop_batching(_z_transport_t *zt) { _z_transport_common_t *ztc = _z_transport_get_common(zt); if (ztc == NULL) { _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #if Z_FEATURE_BATCH_TX_MUTEX == 1 _z_transport_tx_mutex_unlock(ztc); #endif #if Z_FEATURE_BATCH_PEER_MUTEX == 1 _z_transport_peer_mutex_unlock(ztc); #endif ztc->_batch_state = _Z_BATCHING_IDLE; return _Z_RES_OK; } #endif _z_pending_peers_t _z_pending_peers_null(void) { _z_pending_peers_t pending_peers; pending_peers._peers = _z_pending_peer_svec_null(); pending_peers._timeout_ms = 0; pending_peers._start = (z_clock_t){0}; pending_peers._sleep_ms = _Z_SLEEP_BACKOFF_MIN_MS; return pending_peers; } z_result_t _z_pending_peers_copy_from_locators(_z_pending_peers_t *pending_peers, const _z_string_svec_t *locators) { if ((pending_peers == NULL) || (locators == NULL)) { return _Z_ERR_GENERIC; } _z_pending_peers_clear(pending_peers); size_t len = _z_string_svec_len(locators); if (len == 0) { return _Z_RES_OK; } pending_peers->_peers = _z_pending_peer_svec_make(len); if (pending_peers->_peers._val == NULL) { return _Z_ERR_SYSTEM_OUT_OF_MEMORY; } for (size_t i = 0; i < len; i++) { _z_pending_peer_t peer = {0}; peer._state = _Z_PENDING_PEER_STATE_PENDING; _z_string_t *locator = _z_string_svec_get(locators, i); z_result_t ret = _z_string_copy(&peer._locator, locator); if (ret == _Z_RES_OK) { ret = _z_pending_peer_svec_append(&pending_peers->_peers, &peer, false); } if (ret != _Z_RES_OK) { _z_pending_peer_clear(&peer); _z_pending_peers_clear(pending_peers); return ret; } } return _Z_RES_OK; } bool _z_pending_peers_has_pending(const _z_pending_peers_t *pending_peers) { if (pending_peers == NULL) { return false; } size_t len = _z_pending_peer_svec_len(&pending_peers->_peers); for (size_t i = 0; i < len; i++) { const _z_pending_peer_t *peer = _z_pending_peer_svec_get(&pending_peers->_peers, i); if (peer->_state == _Z_PENDING_PEER_STATE_PENDING) { return true; } } return false; } void _z_pending_peers_clear(_z_pending_peers_t *pending_peers) { if (pending_peers == NULL) { return; } _z_pending_peer_svec_clear(&pending_peers->_peers); *pending_peers = _z_pending_peers_null(); } void _z_pending_peers_move(_z_pending_peers_t *dst, _z_pending_peers_t *src) { if ((dst == NULL) || (src == NULL) || (dst == src)) { return; } _z_pending_peers_clear(dst); *dst = *src; *src = _z_pending_peers_null(); } ================================================ FILE: src/transport/unicast/accept.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/link/transport/socket.h" #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/transport/tls_stream.h" #endif #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/lease.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_UNICAST_PEER == 1 && \ (Z_FEATURE_LINK_TCP == 1 || Z_FEATURE_LINK_TLS == 1) #if Z_FEATURE_CONNECTIVITY == 1 static void _zp_unicast_dispatch_connected_event(_z_transport_unicast_t *ztu, const _z_transport_peer_unicast_t *peer) { if (ztu == NULL || peer == NULL) { return; } _z_connectivity_peer_event_data_t connected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; bool has_event_data = false; _z_transport_peer_mutex_lock(&ztu->_common); _z_transport_peer_unicast_slist_t *it = ztu->_peers; while (it != NULL) { _z_transport_peer_unicast_t *current_peer = _z_transport_peer_unicast_slist_value(it); if (current_peer == peer) { _z_transport_get_link_properties(&ztu->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&connected_peer, ¤t_peer->common); has_event_data = true; break; } it = _z_transport_peer_unicast_slist_next(it); } _z_transport_peer_mutex_unlock(&ztu->_common); if (has_event_data) { _z_connectivity_peer_connected(_z_transport_common_get_session(&ztu->_common), &connected_peer, false, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&connected_peer); } } #endif _z_fut_fn_result_t _zp_unicast_accept_task_fn(void *ctx, _z_executor_t *executor) { _ZP_UNUSED(executor); _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)ctx; const _z_sys_net_socket_t *socket_ptr = _z_link_get_socket(ztu->_common._link); if (socket_ptr == NULL) { _Z_ERROR_LOG(_Z_ERR_INVALID); return _z_fut_fn_result_ready(); } _z_sys_net_socket_t listen_socket = *socket_ptr; _z_sys_net_socket_t con_socket = {0}; z_result_t ret = _z_tcp_accept(&listen_socket, &con_socket); if (ret != _Z_RES_OK) { if (ret == _Z_ERR_INVALID) { _Z_INFO("Accept socket was closed"); return _z_fut_fn_result_ready(); } return _z_fut_fn_result_wake_up_after(1000); } if (_z_transport_peer_unicast_slist_len(ztu->_peers) >= Z_LISTEN_MAX_CONNECTION_NB) { _Z_INFO("Refusing connection as max connections currently reached"); #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&con_socket); #endif _z_socket_close(&con_socket); return _z_fut_fn_result_wake_up_after(1000); } ret = _z_socket_set_blocking(&con_socket, true); if (ret != _Z_RES_OK) { _Z_INFO("Failed to set socket blocking with error %d", ret); #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&con_socket); #endif _z_socket_close(&con_socket); return _z_fut_fn_result_continue(); } #if Z_FEATURE_LINK_TLS == 1 if (ztu->_common._link->_type == _Z_LINK_TYPE_TLS) { ret = _z_tls_accept(&con_socket, &listen_socket); if (ret != _Z_RES_OK) { _Z_INFO("TLS handshake failed with error %d", ret); _z_close_tls_socket(&con_socket); _z_socket_close(&con_socket); return _z_fut_fn_result_continue(); } } #endif _z_transport_unicast_establish_param_t param = {0}; ret = _z_unicast_handshake_listen(¶m, ztu->_common._link, &_z_transport_common_get_session(&ztu->_common)->_local_zid, Z_WHATAMI_PEER, &con_socket); if (ret != _Z_RES_OK) { _Z_INFO("Connection accept handshake failed with error %d", ret); #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&con_socket); #endif _z_socket_close(&con_socket); return _z_fut_fn_result_continue(); } if (_z_socket_set_blocking(&con_socket, false) != _Z_RES_OK) { _Z_INFO("Failed to set socket non blocking"); #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&con_socket); #endif _z_socket_close(&con_socket); return _z_fut_fn_result_continue(); } _z_transport_peer_unicast_t *new_peer = NULL; ret = _z_transport_peer_unicast_add(ztu, ¶m, con_socket, true, &new_peer); if (ret != _Z_RES_OK) { #if Z_FEATURE_LINK_TLS == 1 _z_close_tls_socket(&con_socket); #endif _z_socket_close(&con_socket); return _z_fut_fn_result_continue(); } if (new_peer != NULL) { (void)_z_interest_push_declarations_to_peer(_z_transport_common_get_session(&ztu->_common), (void *)new_peer); #if Z_FEATURE_CONNECTIVITY == 1 _zp_unicast_dispatch_connected_event(ztu, new_peer); #endif } return _z_fut_fn_result_continue(); } #endif ================================================ FILE: src/transport/unicast/lease.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/unicast/lease.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/session/liveliness.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_UNICAST_TRANSPORT == 1 #if Z_FEATURE_UNICAST_PEER == 1 static bool _zp_unicast_peer_is_expired(const _z_transport_peer_unicast_t *target, const _z_transport_peer_unicast_t *peer) { _ZP_UNUSED(target); return !peer->common._received; } static void _zp_unicast_report_disconnected_peers(_z_transport_unicast_t *ztu, _z_transport_peer_unicast_slist_t **dropped_peers) { if (dropped_peers == NULL || *dropped_peers == NULL) { return; } #if Z_FEATURE_CONNECTIVITY == 1 uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztu->_common, &mtu, &is_streamed, &is_reliable); #endif _z_session_t *zs = _z_transport_common_get_session(&ztu->_common); _z_transport_peer_unicast_slist_t *it = *dropped_peers; while (it != NULL) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(it); _Z_INFO("Deleting peer because it has expired after %zums", ztu->_common._lease); _z_interest_peer_disconnected(zs, &peer->common); #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; _z_connectivity_peer_event_data_alias_from_common(&disconnected_peer, &peer->common); _z_connectivity_peer_disconnected(zs, &disconnected_peer, false, mtu, is_streamed, is_reliable); #endif it = _z_transport_peer_unicast_slist_next(it); } _z_transport_peer_unicast_slist_free(dropped_peers); } #endif z_result_t _zp_unicast_send_keep_alive(_z_transport_unicast_t *ztu) { z_result_t ret = _Z_RES_OK; _z_transport_message_t t_msg = _z_t_msg_make_keep_alive(); ret = _z_transport_tx_send_t_msg(&ztu->_common, &t_msg, NULL); return ret; } _z_fut_fn_result_t _zp_unicast_failed_result(_z_transport_unicast_t *ztu, _z_executor_t *executor) { _z_session_t *zs = _z_transport_common_get_session(&ztu->_common); #if Z_FEATURE_LIVELINESS == 1 && Z_FEATURE_SUBSCRIPTION == 1 _z_liveliness_subscription_undeclare_all(zs); #endif #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; bool has_disconnected_peer = false; _z_transport_peer_mutex_lock(&ztu->_common); if (!_z_transport_peer_unicast_slist_is_empty(ztu->_peers)) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(ztu->_peers); _z_transport_get_link_properties(&ztu->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&disconnected_peer, &curr_peer->common); has_disconnected_peer = true; } _z_transport_peer_mutex_unlock(&ztu->_common); if (has_disconnected_peer) { _z_connectivity_peer_disconnected(zs, &disconnected_peer, false, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&disconnected_peer); } #endif _z_unicast_transport_close(ztu, _Z_CLOSE_EXPIRED); _z_session_transport_mutex_lock(zs); #if Z_FEATURE_AUTO_RECONNECT == 1 // Store weak session to reuse for reconnection. _z_session_weak_t weak_session_clone = _z_session_weak_clone(&ztu->_common._session); #endif _z_transport_clear(&zs->_tp); _z_session_transport_mutex_unlock(zs); #if Z_FEATURE_AUTO_RECONNECT == 1 ztu->_common._state = _Z_TRANSPORT_STATE_RECONNECTING; ztu->_common._session = weak_session_clone; _z_fut_t f = _z_fut_null(); f._fut_arg = &ztu->_common; f._fut_fn = _z_client_reopen_task_fn; f._destroy_fn = _z_client_reopen_task_drop; if (_z_fut_handle_is_null(_z_executor_spawn(executor, &f))) { _Z_ERROR("Failed to spawn client reopen task after transport failure."); ztu->_common._state = _Z_TRANSPORT_STATE_CLOSED; _z_session_weak_drop(&ztu->_common._session); return _z_fut_fn_result_ready(); } else { return _z_fut_fn_result_suspend(); } #else return _z_fut_fn_result_ready(); #endif } _z_fut_fn_result_t _zp_unicast_lease_task_fn(void *ztu_arg, _z_executor_t *executor) { _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)ztu_arg; if (ztu->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztu->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } z_whatami_t mode = _z_transport_common_get_session(&ztu->_common)->_mode; if (mode == Z_WHATAMI_CLIENT) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(ztu->_peers); assert(curr_peer != NULL); if (curr_peer->common._received) { // Reset the lease parameters curr_peer->common._received = false; return _z_fut_fn_result_wake_up_after((unsigned long)ztu->_common._lease); } else { // THIS LOG STRING USED IN TEST, change with caution _Z_INFO("Closing session because it has expired after %zums", ztu->_common._lease); return _zp_unicast_failed_result(ztu, executor); } } // TODO: Should we have a task per peer ? #if Z_FEATURE_UNICAST_PEER == 1 if (mode == Z_WHATAMI_PEER) { _z_transport_peer_unicast_slist_t *dropped_peers = _z_transport_peer_unicast_slist_new(); _z_transport_peer_mutex_lock(&ztu->_common); ztu->_peers = _z_transport_peer_unicast_slist_extract_all_filter(ztu->_peers, &dropped_peers, _zp_unicast_peer_is_expired, NULL); _z_transport_peer_unicast_slist_t *curr_list = ztu->_peers; while (curr_list != NULL) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(curr_list); curr_peer->common._received = false; curr_list = _z_transport_peer_unicast_slist_next(curr_list); } _z_transport_peer_mutex_unlock(&ztu->_common); _zp_unicast_report_disconnected_peers(ztu, &dropped_peers); return _z_fut_fn_result_wake_up_after((unsigned long)ztu->_common._lease); } #endif return _z_fut_fn_result_ready(); } _z_fut_fn_result_t _zp_unicast_keep_alive_task_fn(void *ztu_arg, _z_executor_t *executor) { _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)ztu_arg; if (ztu->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztu->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } z_whatami_t mode = _z_transport_common_get_session(&ztu->_common)->_mode; if (mode == Z_WHATAMI_CLIENT) { assert(_z_transport_peer_unicast_slist_value(ztu->_peers) != NULL); if (!ztu->_common._transmitted) { if (_zp_unicast_send_keep_alive(ztu) < 0) { // THIS LOG STRING USED IN TEST, change with caution _Z_INFO("Send keep alive failed."); return _zp_unicast_failed_result(ztu, executor); } } ztu->_common._transmitted = false; return _z_fut_fn_result_wake_up_after((unsigned long)ztu->_common._lease / Z_TRANSPORT_LEASE_EXPIRE_FACTOR); } // TODO: Should we have a task per peer ? #if Z_FEATURE_UNICAST_PEER == 1 if (mode == Z_WHATAMI_PEER) { if (!ztu->_common._transmitted) { _Z_DEBUG("Sending keep alive"); // Send keep alive to all peers _z_transport_message_t t_msg = _z_t_msg_make_keep_alive(); _z_transport_peer_mutex_lock(&ztu->_common); if (!_z_transport_peer_unicast_slist_is_empty(ztu->_peers)) { if (_z_transport_tx_send_t_msg(&ztu->_common, &t_msg, ztu->_peers) != _Z_RES_OK) { _Z_INFO("Send keep alive failed."); // TODO: report failed peers and close them ? } } _z_transport_peer_mutex_unlock(&ztu->_common); } ztu->_common._transmitted = false; return _z_fut_fn_result_wake_up_after((unsigned long)ztu->_common._lease / Z_TRANSPORT_LEASE_EXPIRE_FACTOR); } #endif return _z_fut_fn_result_ready(); } #endif // Z_FEATURE_UNICAST_TRANSPORT == 1 ================================================ FILE: src/transport/unicast/read.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/unicast/read.h" #include #include #include "zenoh-pico/api/types.h" #include "zenoh-pico/config.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/link/transport/socket.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/runtime/runtime.h" #include "zenoh-pico/session/interest.h" #include "zenoh-pico/transport/common/rx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/lease.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/utils/logging.h" #define _Z_UNICAST_PEER_READ_STATUS_OK 0 #define _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA -1 #define _Z_UNICAST_PEER_READ_STATUS_SOCKET_CLOSED -2 #define _Z_UNICAST_PEER_READ_STATUS_CRITICAL_ERROR -3 #if Z_FEATURE_UNICAST_TRANSPORT == 1 static z_result_t _z_unicast_process_messages(_z_transport_unicast_t *ztu, _z_transport_peer_unicast_t *peer, size_t to_read) { // Wrap the main buffer to_read bytes _z_zbuf_t zbuf; if (peer->flow_state == _Z_FLOW_STATE_READY) { zbuf = _z_zbuf_view(&peer->flow_buff, to_read); } else { zbuf = _z_zbuf_view(&ztu->_common._zbuf, to_read); } peer->common._received = true; while (_z_zbuf_len(&zbuf) > 0) { // Decode one session message _z_transport_message_t t_msg; z_result_t ret = _z_transport_message_decode(&t_msg, &zbuf); if (ret != _Z_RES_OK) { _Z_INFO("Connection compromised due to malformed message: %d", ret); return ret; } ret = _z_unicast_handle_transport_message(ztu, &t_msg, peer); if (ret != _Z_RES_OK) { if (ret != _Z_ERR_CONNECTION_CLOSED) { _Z_WARN("Connection compromised due to message processing error: %d", ret); } return ret; } } // Move the read position of the read buffer if (peer->flow_state == _Z_FLOW_STATE_READY) { _z_zbuf_set_rpos(&peer->flow_buff, _z_zbuf_get_rpos(&peer->flow_buff) + to_read); } else { _z_zbuf_set_rpos(&ztu->_common._zbuf, _z_zbuf_get_rpos(&ztu->_common._zbuf) + to_read); } if (_z_unicast_update_rx_buffer(ztu) != _Z_RES_OK) { _Z_ERROR("Connection closed due to lack of memory to allocate rx buffer"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } return _Z_RES_OK; } static bool _z_unicast_client_read(_z_transport_unicast_t *ztu, _z_transport_peer_unicast_t *peer, size_t *to_read) { switch (ztu->_common._link->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: if (_z_zbuf_len(&ztu->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (_z_zbuf_len(&ztu->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_zbuf_compact(&ztu->_common._zbuf); return false; } } // Get stream size *to_read = _z_read_stream_size(&ztu->_common._zbuf); // Read data if (_z_zbuf_len(&ztu->_common._zbuf) < *to_read) { _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (_z_zbuf_len(&ztu->_common._zbuf) < *to_read) { _z_zbuf_set_rpos(&ztu->_common._zbuf, _z_zbuf_get_rpos(&ztu->_common._zbuf) - _Z_MSG_LEN_ENC_SIZE); _z_zbuf_compact(&ztu->_common._zbuf); return false; } } break; case Z_LINK_CAP_FLOW_DATAGRAM: _z_zbuf_compact(&ztu->_common._zbuf); *to_read = _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (*to_read == SIZE_MAX) { return false; } break; default: break; } return true; } z_result_t _zp_unicast_read(_z_transport_unicast_t *ztu, bool single_read) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(ztu->_peers); if (curr_peer == NULL) { _Z_ERROR("Invalid router endpoint\n"); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_RX_FAILED); } // Read & process a single message if (single_read) { _z_transport_message_t t_msg; _Z_RETURN_IF_ERR(_z_unicast_recv_t_msg(ztu, &t_msg)); _Z_CLEAN_RETURN_IF_ERR(_z_unicast_handle_transport_message(ztu, &t_msg, curr_peer), _z_t_msg_clear(&t_msg)); _z_t_msg_clear(&t_msg); // Update buffer _Z_RETURN_IF_ERR(_z_unicast_update_rx_buffer(ztu)); } else { // Prepare buffer _z_zbuf_reset(&ztu->_common._zbuf); size_t to_read = 0; // Retrieve data if any if (_z_unicast_client_read(ztu, curr_peer, &to_read)) { // Process data _Z_RETURN_IF_ERR(_z_unicast_process_messages(ztu, curr_peer, to_read)) } else { return _Z_NO_DATA_PROCESSED; } } return _Z_RES_OK; } #if Z_FEATURE_UNICAST_PEER == 1 static void _z_unicast_wait_iter_reset(_z_socket_wait_iter_t *iter) { iter->_current_entry = NULL; } static bool _z_unicast_wait_iter_next(_z_socket_wait_iter_t *iter) { _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)iter->_ctx; if (iter->_current_entry == NULL) { iter->_current_entry = ztu->_peers; } else { iter->_current_entry = _z_transport_peer_unicast_slist_next((_z_transport_peer_unicast_slist_t *)iter->_current_entry); } return iter->_current_entry != NULL; } static const _z_sys_net_socket_t *_z_unicast_wait_iter_get_socket(const _z_socket_wait_iter_t *iter) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value((_z_transport_peer_unicast_slist_t *)iter->_current_entry); return &peer->_socket; } static void _z_unicast_wait_iter_set_ready(_z_socket_wait_iter_t *iter, bool ready) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value((_z_transport_peer_unicast_slist_t *)iter->_current_entry); peer->_pending = ready; } static z_result_t _z_unicast_wait_peer_event(_z_transport_unicast_t *ztu) { _z_socket_wait_iter_t iter = { ._ctx = ztu, ._current_entry = NULL, ._reset = _z_unicast_wait_iter_reset, ._next = _z_unicast_wait_iter_next, ._get_socket = _z_unicast_wait_iter_get_socket, ._set_ready = _z_unicast_wait_iter_set_ready, }; return _z_socket_wait_readable(&iter, Z_CONFIG_SOCKET_TIMEOUT); } static z_result_t _z_unicast_handle_remaining_data(_z_transport_unicast_t *ztu, _z_transport_peer_unicast_t *peer, size_t extra_size, size_t *to_read, bool *message_to_process) { *message_to_process = false; if (extra_size < _Z_MSG_LEN_ENC_SIZE) { peer->flow_state = _Z_FLOW_STATE_PENDING_SIZE; peer->flow_curr_size = _z_zbuf_read(&ztu->_common._zbuf); return _Z_RES_OK; } // Get stream size *to_read = _z_read_stream_size(&ztu->_common._zbuf); if (_z_zbuf_len(&ztu->_common._zbuf) < *to_read) { peer->flow_state = _Z_FLOW_STATE_PENDING_DATA; peer->flow_curr_size = (uint16_t)*to_read; peer->flow_buff = _z_zbuf_make(peer->flow_curr_size); if (_z_zbuf_capacity(&peer->flow_buff) != peer->flow_curr_size) { _Z_ERROR("Not enough memory to allocate flow state buffer"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } _z_zbuf_copy_bytes(&peer->flow_buff, &ztu->_common._zbuf); return _Z_RES_OK; } *message_to_process = true; return _Z_RES_OK; } static int _z_unicast_peer_read(_z_transport_unicast_t *ztu, _z_transport_peer_unicast_t *peer, size_t *to_read) { // If we receive fragmented data we have to store it on a separate buffer size_t read_size = 0; switch (ztu->_common._link->_cap._flow) { case Z_LINK_CAP_FLOW_STREAM: switch (peer->flow_state) { case _Z_FLOW_STATE_READY: peer->flow_state = _Z_FLOW_STATE_INACTIVE; peer->flow_curr_size = 0; _z_zbuf_clear(&peer->flow_buff); // fall through default: // fall through case _Z_FLOW_STATE_INACTIVE: read_size = _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (read_size == 0) { _Z_DEBUG("Socket closed"); return _Z_UNICAST_PEER_READ_STATUS_SOCKET_CLOSED; } else if (read_size == SIZE_MAX) { return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } if (_z_zbuf_len(&ztu->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { peer->flow_state = _Z_FLOW_STATE_PENDING_SIZE; peer->flow_curr_size = _z_zbuf_read(&ztu->_common._zbuf); return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } // Get stream size *to_read = _z_read_stream_size(&ztu->_common._zbuf); // Read data if needed read_size = _z_zbuf_len(&ztu->_common._zbuf); if (read_size < *to_read) { peer->flow_state = _Z_FLOW_STATE_PENDING_DATA; peer->flow_curr_size = (uint16_t)*to_read; peer->flow_buff = _z_zbuf_make(peer->flow_curr_size); if (_z_zbuf_capacity(&peer->flow_buff) != peer->flow_curr_size) { _Z_ERROR("Not enough memory to allocate flow state buffer"); return _Z_UNICAST_PEER_READ_STATUS_CRITICAL_ERROR; } _z_zbuf_copy_bytes(&peer->flow_buff, &ztu->_common._zbuf); return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } break; case _Z_FLOW_STATE_PENDING_SIZE: read_size = _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (read_size == 0) { _Z_DEBUG("Socket closed"); return _Z_UNICAST_PEER_READ_STATUS_SOCKET_CLOSED; } else if (read_size == SIZE_MAX) { return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } peer->flow_curr_size += (uint16_t)(_z_zbuf_read(&ztu->_common._zbuf) << 8); *to_read = peer->flow_curr_size; if (_z_zbuf_len(&ztu->_common._zbuf) < *to_read) { peer->flow_state = _Z_FLOW_STATE_PENDING_DATA; peer->flow_buff = _z_zbuf_make(peer->flow_curr_size); if (_z_zbuf_capacity(&peer->flow_buff) != peer->flow_curr_size) { _Z_ERROR("Not enough memory to allocate flow state buffer"); return _Z_UNICAST_PEER_READ_STATUS_CRITICAL_ERROR; } _z_zbuf_copy_bytes(&peer->flow_buff, &ztu->_common._zbuf); return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } break; case _Z_FLOW_STATE_PENDING_DATA: read_size = _z_link_socket_recv_zbuf(ztu->_common._link, &peer->flow_buff, peer->_socket); if (read_size == 0) { _Z_DEBUG("Socket closed"); return _Z_UNICAST_PEER_READ_STATUS_SOCKET_CLOSED; } else if (read_size == SIZE_MAX) { return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } *to_read = peer->flow_curr_size; if (_z_zbuf_len(&peer->flow_buff) < *to_read) { return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } else { peer->flow_state = _Z_FLOW_STATE_READY; } break; } break; case Z_LINK_CAP_FLOW_DATAGRAM: *to_read = _z_link_socket_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, peer->_socket); if (*to_read == SIZE_MAX) { return _Z_UNICAST_PEER_READ_STATUS_PENDING_DATA; } break; default: break; } return _Z_UNICAST_PEER_READ_STATUS_OK; } static z_result_t _zp_unicast_process_peer_event(_z_transport_unicast_t *ztu) { _z_transport_peer_mutex_lock(&ztu->_common); _z_transport_peer_unicast_slist_t *curr_list = ztu->_peers; _z_transport_peer_unicast_slist_t *prev = NULL; _z_transport_peer_unicast_slist_t *prev_drop = NULL; size_t to_read = 0; while (curr_list != NULL) { bool drop_peer = false; _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(curr_list); if (curr_peer->_pending) { curr_peer->_pending = false; // Read data from socket int res = _z_unicast_peer_read(ztu, curr_peer, &to_read); if (res == _Z_UNICAST_PEER_READ_STATUS_OK) { // Messages to process bool message_to_process = false; do { message_to_process = false; // Process one message if (_z_unicast_process_messages(ztu, curr_peer, to_read) != _Z_RES_OK) { // Failed to process, drop peer _Z_ERROR("Dropping peer due to processing error"); drop_peer = true; prev_drop = prev; break; } else if (curr_peer->flow_state != _Z_FLOW_STATE_READY) { // Process remaining data size_t extra_data = _z_zbuf_len(&ztu->_common._zbuf); if (extra_data > 0) { _Z_RETURN_IF_ERR(_z_unicast_handle_remaining_data(ztu, curr_peer, extra_data, &to_read, &message_to_process)); } } } while (message_to_process); } else if (res == _Z_UNICAST_PEER_READ_STATUS_SOCKET_CLOSED) { drop_peer = true; prev_drop = prev; } else if (res == _Z_UNICAST_PEER_READ_STATUS_CRITICAL_ERROR) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } } // Update previous only if current node is not dropped if (!drop_peer) { prev = curr_list; } // Progress list curr_list = _z_transport_peer_unicast_slist_next(curr_list); // Drop peer if needed if (drop_peer) { _Z_DEBUG("Dropping peer"); _z_session_t *zs = _z_transport_common_get_session(&ztu->_common); #if Z_FEATURE_CONNECTIVITY == 1 _z_connectivity_peer_event_data_t disconnected_peer = {0}; uint16_t mtu = 0; bool is_streamed = false; bool is_reliable = false; _z_transport_get_link_properties(&ztu->_common, &mtu, &is_streamed, &is_reliable); _z_connectivity_peer_event_data_copy_from_common(&disconnected_peer, &curr_peer->common); #endif _z_interest_peer_disconnected(zs, &curr_peer->common); ztu->_peers = _z_transport_peer_unicast_slist_drop_element(ztu->_peers, prev_drop); #if Z_FEATURE_CONNECTIVITY == 1 _z_transport_peer_mutex_unlock(&ztu->_common); _z_connectivity_peer_disconnected(zs, &disconnected_peer, false, mtu, is_streamed, is_reliable); _z_connectivity_peer_event_data_clear(&disconnected_peer); _z_transport_peer_mutex_lock(&ztu->_common); curr_list = ztu->_peers; prev = NULL; prev_drop = NULL; _z_zbuf_reset(&ztu->_common._zbuf); continue; #endif } _z_zbuf_reset(&ztu->_common._zbuf); } _z_transport_peer_mutex_unlock(&ztu->_common); return _Z_RES_OK; } #endif _z_fut_fn_result_t _zp_unicast_read_task_fn(void *ztu_arg, _z_executor_t *executor) { _z_transport_unicast_t *ztu = (_z_transport_unicast_t *)ztu_arg; if (ztu->_common._state == _Z_TRANSPORT_STATE_CLOSED) { return _z_fut_fn_result_ready(); } else if (ztu->_common._state == _Z_TRANSPORT_STATE_RECONNECTING) { return _z_fut_fn_result_suspend(); } z_whatami_t mode = _z_transport_common_get_session(&ztu->_common)->_mode; if (mode == Z_WHATAMI_CLIENT) { _z_transport_peer_unicast_t *curr_peer = _z_transport_peer_unicast_slist_value(ztu->_peers); assert(curr_peer != NULL); size_t to_read = 0; // Retrieve data if (_z_unicast_client_read(ztu, curr_peer, &to_read) && _z_unicast_process_messages(ztu, curr_peer, to_read) != _Z_RES_OK) { _Z_INFO("Read task failed, closing session\n"); return _zp_unicast_failed_result(ztu, executor); } } #if Z_FEATURE_UNICAST_PEER == 1 if (mode == Z_WHATAMI_PEER) { bool has_peers = !_z_transport_peer_unicast_slist_is_empty(ztu->_peers); if (!has_peers) { return _z_fut_fn_result_wake_up_after(100); } if (_z_unicast_wait_peer_event(ztu) == _Z_RES_OK && _zp_unicast_process_peer_event(ztu) != _Z_RES_OK) { // TODO: Close transport on error. Probably we should just close the failed peer and // initiate reconnection task. return _z_fut_fn_result_ready(); } } #endif return _z_fut_fn_result_continue(); } #endif // Z_FEATURE_UNICAST_TRANSPORT == 1 ================================================ FILE: src/transport/unicast/rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/common/rx.h" #include #include "zenoh-pico/config.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_UNICAST_TRANSPORT == 1 z_result_t _z_unicast_recv_t_msg(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg) { _Z_DEBUG(">> recv session msg"); z_result_t ret = _Z_RES_OK; size_t to_read = 0; _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(ztu->_peers); do { switch (ztu->_common._link->_cap._flow) { // Stream capable links case Z_LINK_CAP_FLOW_STREAM: if (_z_zbuf_len(&ztu->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_link_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, NULL); if (_z_zbuf_len(&ztu->_common._zbuf) < _Z_MSG_LEN_ENC_SIZE) { _z_zbuf_compact(&ztu->_common._zbuf); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES); ret = _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES; continue; } } // Get stream size to_read = _z_read_stream_size(&ztu->_common._zbuf); // Read data if (_z_zbuf_len(&ztu->_common._zbuf) < to_read) { _z_link_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, NULL); if (_z_zbuf_len(&ztu->_common._zbuf) < to_read) { _z_zbuf_set_rpos(&ztu->_common._zbuf, _z_zbuf_get_rpos(&ztu->_common._zbuf) - _Z_MSG_LEN_ENC_SIZE); _z_zbuf_compact(&ztu->_common._zbuf); _Z_ERROR_LOG(_Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES); ret = _Z_ERR_TRANSPORT_NOT_ENOUGH_BYTES; continue; } } break; // Datagram capable links case Z_LINK_CAP_FLOW_DATAGRAM: _z_zbuf_compact(&ztu->_common._zbuf); to_read = _z_link_recv_zbuf(ztu->_common._link, &ztu->_common._zbuf, NULL); if (to_read == SIZE_MAX) { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_RX_FAILED); ret = _Z_ERR_TRANSPORT_RX_FAILED; } break; default: break; } } while (false); // The 1-iteration loop to use continue to break the entire loop on error if (ret == _Z_RES_OK) { _Z_DEBUG(">> \t transport_message_decode"); // Wrap the main buffer to_read bytes _z_zbuf_t zbuf = _z_zbuf_view(&ztu->_common._zbuf, to_read); ret = _z_transport_message_decode(t_msg, &zbuf); if (ret == _Z_RES_OK) { // Mark the session that we have received data peer->common._received = true; // Update the actual buffer pointers _z_zbuf_set_rpos(&ztu->_common._zbuf, _z_zbuf_get_rpos(&ztu->_common._zbuf) + _z_zbuf_get_rpos(&zbuf)); } else { _Z_ERROR("Malformed transport message: %d", ret); _z_zbuf_set_rpos(&ztu->_common._zbuf, _z_zbuf_get_rpos(&ztu->_common._zbuf) + to_read); } } return ret; } static z_result_t _z_unicast_handle_frame(_z_transport_unicast_t *ztu, uint8_t header, _z_t_msg_frame_t *msg, _z_transport_peer_unicast_t *peer) { z_reliability_t tmsg_reliability; // Check if the SN is correct if (_Z_HAS_FLAG(header, _Z_FLAG_T_FRAME_R)) { tmsg_reliability = Z_RELIABILITY_RELIABLE; // @TODO: amend once reliability is in place. For the time being only // monotonic SNs are ensured if (_z_sn_precedes(ztu->_common._sn_res, peer->_sn_rx_reliable, msg->_sn)) { peer->_sn_rx_reliable = msg->_sn; } else { #if Z_FEATURE_FRAGMENTATION == 1 _z_wbuf_clear(&peer->common._dbuf_reliable); peer->common._state_reliable = _Z_DBUF_STATE_NULL; #endif _Z_INFO("Reliable message dropped because it is out of order"); _z_t_msg_frame_clear(msg); return _Z_RES_OK; } } else { tmsg_reliability = Z_RELIABILITY_BEST_EFFORT; if (_z_sn_precedes(ztu->_common._sn_res, peer->_sn_rx_best_effort, msg->_sn)) { peer->_sn_rx_best_effort = msg->_sn; } else { #if Z_FEATURE_FRAGMENTATION == 1 _z_wbuf_clear(&peer->common._dbuf_best_effort); peer->common._state_best_effort = _Z_DBUF_STATE_NULL; #endif _Z_INFO("Best effort message dropped because it is out of order"); _z_t_msg_frame_clear(msg); return _Z_RES_OK; } } // Handle all the zenoh message, one by one // From this point, memory cleaning must be handled by the network message layer _z_network_message_t curr_nmsg = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); while (_z_zbuf_len(msg->_payload) > 0) { _Z_RETURN_IF_ERR(_z_network_message_decode(&curr_nmsg, msg->_payload, &arcs, (uintptr_t)&peer->common)); curr_nmsg._reliability = tmsg_reliability; _Z_RETURN_IF_ERR(_z_handle_network_message(&ztu->_common, &curr_nmsg, &peer->common)); } return _Z_RES_OK; } static z_result_t _z_unicast_handle_fragment_inner(_z_transport_unicast_t *ztu, uint8_t header, _z_t_msg_fragment_t *msg, _z_transport_peer_unicast_t *peer) { z_result_t ret = _Z_RES_OK; #if Z_FEATURE_FRAGMENTATION == 1 _z_wbuf_t *dbuf; uint8_t *dbuf_state; z_reliability_t tmsg_reliability; bool consecutive; // Select the right defragmentation buffer if (_Z_HAS_FLAG(header, _Z_FLAG_T_FRAGMENT_R)) { tmsg_reliability = Z_RELIABILITY_RELIABLE; // Check SN // @TODO: amend once reliability is in place. For the time being only // monotonic SNs are ensured if (_z_sn_precedes(ztu->_common._sn_res, peer->_sn_rx_reliable, msg->_sn)) { consecutive = _z_sn_consecutive(ztu->_common._sn_res, peer->_sn_rx_reliable, msg->_sn); peer->_sn_rx_reliable = msg->_sn; dbuf = &peer->common._dbuf_reliable; dbuf_state = &peer->common._state_reliable; } else { _z_wbuf_clear(&peer->common._dbuf_reliable); peer->common._state_reliable = _Z_DBUF_STATE_NULL; _Z_INFO("Reliable message dropped because it is out of order"); return _Z_RES_OK; } } else { tmsg_reliability = Z_RELIABILITY_BEST_EFFORT; // Check SN if (_z_sn_precedes(ztu->_common._sn_res, peer->_sn_rx_best_effort, msg->_sn)) { consecutive = _z_sn_consecutive(ztu->_common._sn_res, peer->_sn_rx_best_effort, msg->_sn); peer->_sn_rx_best_effort = msg->_sn; dbuf = &peer->common._dbuf_best_effort; dbuf_state = &peer->common._state_best_effort; } else { _z_wbuf_clear(&peer->common._dbuf_best_effort); peer->common._state_best_effort = _Z_DBUF_STATE_NULL; _Z_INFO("Best effort message dropped because it is out of order"); return _Z_RES_OK; } } // Check consecutive SN if (!consecutive && _z_wbuf_len(dbuf) > 0) { _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; _Z_INFO("Defragmentation buffer dropped because non-consecutive fragments received"); return _Z_RES_OK; } // Handle fragment markers if (_Z_PATCH_HAS_FRAGMENT_MARKERS(peer->common._patch)) { if (msg->first) { _z_wbuf_reset(dbuf); } else if (_z_wbuf_len(dbuf) == 0) { _Z_INFO("First fragment received without the start marker"); return _Z_RES_OK; } if (msg->drop) { _z_wbuf_reset(dbuf); return _Z_RES_OK; } } // Allocate buffer if needed if (*dbuf_state == _Z_DBUF_STATE_NULL) { *dbuf = _z_wbuf_make(Z_FRAG_MAX_SIZE, false); if (_z_wbuf_capacity(dbuf) != Z_FRAG_MAX_SIZE) { _Z_ERROR("Not enough memory to allocate transport defragmentation buffer"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } *dbuf_state = _Z_DBUF_STATE_INIT; } // Process fragment data if (*dbuf_state == _Z_DBUF_STATE_INIT) { // Check overflow if ((_z_wbuf_len(dbuf) + msg->_payload.len) > Z_FRAG_MAX_SIZE) { *dbuf_state = _Z_DBUF_STATE_OVERFLOW; } else { // Fill buffer _z_wbuf_write_bytes(dbuf, msg->_payload.start, 0, msg->_payload.len); } } // Process final fragment if (!_Z_HAS_FLAG(header, _Z_FLAG_T_FRAGMENT_M)) { // Drop message if it exceeds the fragmentation size if (*dbuf_state == _Z_DBUF_STATE_OVERFLOW) { _Z_INFO("Fragment dropped because defragmentation buffer has overflown"); _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; return _Z_RES_OK; } // Convert the defragmentation buffer into a decoding buffer _z_zbuf_t zbf = _z_wbuf_moved_as_zbuf(dbuf); if (_z_zbuf_capacity(&zbf) == 0) { _Z_ERROR("Failed to convert defragmentation buffer into a decoding buffer!"); _z_wbuf_clear(dbuf); *dbuf_state = _Z_DBUF_STATE_NULL; _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Decode message _z_zenoh_message_t zm = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); ret = _z_network_message_decode(&zm, &zbf, &arcs, (uintptr_t)&peer->common); zm._reliability = tmsg_reliability; if (ret == _Z_RES_OK) { // Memory clear of the network message data must be handled by the network message layer _z_handle_network_message(&ztu->_common, &zm, &peer->common); } else { _Z_INFO("Failed to decode defragmented message"); _Z_ERROR_LOG(_Z_ERR_MESSAGE_DESERIALIZATION_FAILED); ret = _Z_ERR_MESSAGE_DESERIALIZATION_FAILED; } // Free the decoding buffer _z_zbuf_clear(&zbf); *dbuf_state = _Z_DBUF_STATE_NULL; } #else _ZP_UNUSED(ztu); _ZP_UNUSED(header); _ZP_UNUSED(msg); _ZP_UNUSED(peer); _Z_INFO("Fragment dropped because fragmentation feature is deactivated"); #endif return ret; } static z_result_t _z_unicast_handle_fragment(_z_transport_unicast_t *ztu, uint8_t header, _z_t_msg_fragment_t *msg, _z_transport_peer_unicast_t *peer) { z_result_t ret = _z_unicast_handle_fragment_inner(ztu, header, msg, peer); _z_t_msg_fragment_clear(msg); return ret; } z_result_t _z_unicast_handle_transport_message(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg, _z_transport_peer_unicast_t *peer) { z_result_t ret = _Z_RES_OK; switch (_Z_MID(t_msg->_header)) { case _Z_MID_T_FRAME: _Z_DEBUG("Received Z_FRAME message"); ret = _z_unicast_handle_frame(ztu, t_msg->_header, &t_msg->_body._frame, peer); break; case _Z_MID_T_FRAGMENT: _Z_DEBUG("Received Z_FRAGMENT message"); ret = _z_unicast_handle_fragment(ztu, t_msg->_header, &t_msg->_body._fragment, peer); break; case _Z_MID_T_KEEP_ALIVE: { _Z_DEBUG("Received Z_KEEP_ALIVE message"); _z_t_msg_keep_alive_clear(&t_msg->_body._keep_alive); break; } case _Z_MID_T_INIT: { // Do nothing, zenoh clients are not expected to handle accept messages on established sessions _z_t_msg_init_clear(&t_msg->_body._init); break; } case _Z_MID_T_OPEN: { // Do nothing, zenoh clients are not expected to handle accept messages on established sessions _z_t_msg_open_clear(&t_msg->_body._open); break; } case _Z_MID_T_CLOSE: { _Z_INFO("Closing session as requested by the remote peer"); // Peer will be dropped thanks to the error _Z_ERROR_LOG(_Z_ERR_CONNECTION_CLOSED); ret = _Z_ERR_CONNECTION_CLOSED; _z_t_msg_close_clear(&t_msg->_body._close); break; } default: { _Z_INFO("WARNING: Unknown transport message ID"); _z_t_msg_clear(t_msg); break; } } return ret; } z_result_t _z_unicast_update_rx_buffer(_z_transport_unicast_t *ztu) { // Check if user or defragment buffer took ownership of buffer if (_z_zbuf_get_ref_count(&ztu->_common._zbuf) != 1) { // Allocate a new buffer size_t buff_capacity = _z_zbuf_capacity(&ztu->_common._zbuf); _z_zbuf_t new_zbuf = _z_zbuf_make(buff_capacity); if (_z_zbuf_capacity(&new_zbuf) != buff_capacity) { _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Recopy leftover bytes size_t leftovers = _z_zbuf_len(&ztu->_common._zbuf); if (leftovers > 0) { _z_zbuf_copy_bytes(&new_zbuf, &ztu->_common._zbuf); } // Drop buffer & update _z_zbuf_clear(&ztu->_common._zbuf); ztu->_common._zbuf = new_zbuf; } return _Z_RES_OK; } #else z_result_t _z_unicast_recv_t_msg(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg) { _ZP_UNUSED(ztu); _ZP_UNUSED(t_msg); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_unicast_handle_transport_message(_z_transport_unicast_t *ztu, _z_transport_message_t *t_msg, _z_transport_peer_unicast_t *peer) { _ZP_UNUSED(ztu); _ZP_UNUSED(t_msg); _ZP_UNUSED(peer); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } #endif // Z_FEATURE_UNICAST_TRANSPORT == 1 ================================================ FILE: src/transport/unicast/transport.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/transport/common/transport.h" #include #include #include #include #include #include "zenoh-pico/link/link.h" #include "zenoh-pico/system/common/platform.h" #include "zenoh-pico/transport/common/rx.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/transport/unicast/transport.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #if Z_FEATURE_UNICAST_TRANSPORT == 1 static z_result_t _z_unicast_transport_create_inner(_z_transport_unicast_t *ztu, _z_link_t *zl, _z_transport_unicast_establish_param_t *param) { // Initialize batching data #if Z_FEATURE_BATCHING == 1 ztu->_common._batch_state = _Z_BATCHING_IDLE; ztu->_common._batch_count = 0; #endif #if Z_FEATURE_MULTI_THREAD == 1 // Initialize the mutexes _Z_RETURN_IF_ERR(_z_mutex_init(&ztu->_common._mutex_tx)); _Z_RETURN_IF_ERR(_z_mutex_rec_init(&ztu->_common._mutex_peer)); #endif // Z_FEATURE_MULTI_THREAD == 1 // Initialize the read and write buffers uint16_t mtu = (zl->_mtu < param->_batch_size) ? zl->_mtu : param->_batch_size; size_t wbuf_size = mtu; size_t zbuf_size = param->_batch_size; // Initialize tx rx buffers ztu->_common._wbuf = _z_wbuf_make(wbuf_size, false); ztu->_common._zbuf = _z_zbuf_make(zbuf_size); // Check if a buffer failed to allocate if ((_z_wbuf_capacity(&ztu->_common._wbuf) != wbuf_size) || (_z_zbuf_capacity(&ztu->_common._zbuf) != zbuf_size)) { _Z_ERROR("Not enough memory to allocate transport buffers!"); _Z_ERROR_RETURN(_Z_ERR_SYSTEM_OUT_OF_MEMORY); } // Set default SN resolution ztu->_common._sn_res = _z_sn_max(param->_seq_num_res); // The initial SN at TX side ztu->_common._sn_tx_reliable = param->_initial_sn_tx; ztu->_common._sn_tx_best_effort = param->_initial_sn_tx; // Notifiers ztu->_common._transmitted = 0; // Transport lease ztu->_common._lease = param->_lease; // Transport link for unicast ztu->_common._link = zl; ztu->_peers = _z_transport_peer_unicast_slist_new(); ztu->_pending_peers = _z_pending_peers_null(); return _Z_RES_OK; } z_result_t _z_unicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_unicast_establish_param_t *param) { zt->_type = _Z_TRANSPORT_UNICAST_TYPE; _z_transport_unicast_t *ztu = &zt->_transport._unicast; memset(ztu, 0, sizeof(_z_transport_unicast_t)); z_result_t ret = _z_unicast_transport_create_inner(ztu, zl, param); if (ret != _Z_RES_OK) { // Clear alloc data #if Z_FEATURE_MULTI_THREAD == 1 _z_mutex_drop(&ztu->_common._mutex_tx); _z_mutex_rec_drop(&ztu->_common._mutex_peer); #endif _z_wbuf_clear(&ztu->_common._wbuf); _z_zbuf_clear(&ztu->_common._zbuf); } return ret; } static z_result_t _z_unicast_handshake_open(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, z_whatami_t mode, _z_sys_net_socket_t *socket) { z_clock_t recv_deadline = z_clock_now(); z_clock_advance_ms(&recv_deadline, Z_TRANSPORT_CONNECT_TIMEOUT); _z_transport_message_t ism = _z_t_msg_make_init_syn(mode, *local_zid); param->_seq_num_res = ism._body._init._seq_num_res; // The announced sn resolution param->_req_id_res = ism._body._init._req_id_res; // The announced req id resolution param->_batch_size = ism._body._init._batch_size; // The announced batch size // Encode and send the message _Z_DEBUG("Sending Z_INIT(Syn)"); z_result_t ret = _z_link_send_t_msg(zl, &ism, socket); _z_t_msg_clear(&ism); if (ret != _Z_RES_OK) { return ret; } // Try to receive response _z_transport_message_t iam = {0}; _Z_RETURN_IF_ERR(_z_link_recv_t_msg(&iam, zl, socket, recv_deadline)); if ((_Z_MID(iam._header) != _Z_MID_T_INIT) || !_Z_HAS_FLAG(iam._header, _Z_FLAG_T_INIT_A)) { _z_t_msg_clear(&iam); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_UNEXPECTED); } param->_remote_whatami = iam._body._init._whatami; _Z_DEBUG("Received Z_INIT(Ack)"); // Any of the size parameters in the InitAck must be less or equal than the one in the InitSyn, // otherwise the InitAck message is considered invalid and it should be treated as a // CLOSE message with L==0 by the Initiating Peer -- the recipient of the InitAck message. if (iam._body._init._seq_num_res <= param->_seq_num_res) { param->_seq_num_res = iam._body._init._seq_num_res; } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION); ret = _Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION; } if (iam._body._init._req_id_res <= param->_req_id_res) { param->_req_id_res = iam._body._init._req_id_res; } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION); ret = _Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION; } if (iam._body._init._batch_size <= param->_batch_size) { param->_batch_size = iam._body._init._batch_size; } else { _Z_ERROR_LOG(_Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION); ret = _Z_ERR_TRANSPORT_OPEN_SN_RESOLUTION; } #if Z_FEATURE_FRAGMENTATION == 1 if (iam._body._init._patch <= ism._body._init._patch) { param->_patch = iam._body._init._patch; } else { // TODO: Use a better error code? _Z_ERROR_LOG(_Z_ERR_GENERIC); ret = _Z_ERR_GENERIC; } #endif if (ret != _Z_RES_OK) { _z_t_msg_clear(&iam); return ret; } param->_key_id_res = 0x08 << param->_key_id_res; param->_req_id_res = 0x08 << param->_req_id_res; if (mode == Z_WHATAMI_CLIENT) { // The initial SN at TX side z_random_fill(¶m->_initial_sn_tx, sizeof(param->_initial_sn_tx)); param->_initial_sn_tx = param->_initial_sn_tx & !_z_sn_modulo_mask(param->_seq_num_res); } // Should be pre-initialized in peer mode // Initialize the Local and Remote Peer IDs param->_remote_zid = iam._body._init._zid; // Create the OpenSyn message _z_zint_t lease = Z_TRANSPORT_LEASE; _z_zint_t initial_sn = param->_initial_sn_tx; _z_slice_t cookie = _z_slice_null(); if (!_z_slice_is_empty(&iam._body._init._cookie)) { _z_slice_copy(&cookie, &iam._body._init._cookie); } _z_transport_message_t osm = _z_t_msg_make_open_syn(lease, initial_sn, cookie); _z_t_msg_clear(&iam); // Encode and send the message _Z_DEBUG("Sending Z_OPEN(Syn)"); ret = _z_link_send_t_msg(zl, &osm, socket); _z_t_msg_clear(&osm); if (ret != _Z_RES_OK) { return ret; } // Try to receive response _z_transport_message_t oam = {0}; _Z_RETURN_IF_ERR(_z_link_recv_t_msg(&oam, zl, socket, recv_deadline)); if ((_Z_MID(oam._header) != _Z_MID_T_OPEN) || !_Z_HAS_FLAG(oam._header, _Z_FLAG_T_OPEN_A)) { _z_t_msg_clear(&oam); _Z_ERROR_LOG(_Z_ERR_MESSAGE_UNEXPECTED); ret = _Z_ERR_MESSAGE_UNEXPECTED; } // THIS LOG STRING USED IN TEST, change with caution _Z_DEBUG("Received Z_OPEN(Ack)"); param->_lease = (oam._body._open._lease < Z_TRANSPORT_LEASE) ? oam._body._open._lease : Z_TRANSPORT_LEASE; // The initial SN at RX side. Initialize the session as we had already received // a message with a SN equal to initial_sn - 1. param->_initial_sn_rx = oam._body._open._initial_sn; _z_t_msg_clear(&oam); return _Z_RES_OK; } z_result_t _z_unicast_handshake_listen(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, z_whatami_t mode, _z_sys_net_socket_t *socket) { z_clock_t recv_deadline = z_clock_now(); z_clock_advance_ms(&recv_deadline, Z_TRANSPORT_ACCEPT_TIMEOUT); assert(mode == Z_WHATAMI_PEER); // Read t message from link _z_transport_message_t tmsg = {0}; z_result_t ret = _z_link_recv_t_msg(&tmsg, zl, socket, recv_deadline); if (ret != _Z_RES_OK) { return ret; } // Receive InitSyn if (_Z_MID(tmsg._header) != _Z_MID_T_INIT || _Z_HAS_FLAG(tmsg._header, _Z_FLAG_T_INIT_A)) { _z_t_msg_clear(&tmsg); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_UNEXPECTED); } _Z_DEBUG("Received Z_INIT(Syn)"); // Encode InitAck _z_slice_t cookie = _z_slice_null(); _z_transport_message_t iam = _z_t_msg_make_init_ack(mode, *local_zid, cookie); // If the new node has less representing capabilities adjust settings if (tmsg._body._init._seq_num_res < iam._body._init._seq_num_res) { _Z_DEBUG("Adjusting SN resolution from %u to %u", iam._body._init._seq_num_res, tmsg._body._init._seq_num_res); iam._body._init._seq_num_res = tmsg._body._init._seq_num_res; } if (tmsg._body._init._req_id_res < iam._body._init._req_id_res) { _Z_DEBUG("Adjusting Req ID resolution from %u to %u", iam._body._init._req_id_res, tmsg._body._init._req_id_res); iam._body._init._req_id_res = tmsg._body._init._req_id_res; } if (tmsg._body._init._batch_size < iam._body._init._batch_size) { _Z_DEBUG("Adjusting Batch Size from %u to %u", iam._body._init._batch_size, tmsg._body._init._batch_size); iam._body._init._batch_size = tmsg._body._init._batch_size; } #if Z_FEATURE_FRAGMENTATION == 1 if (iam._body._init._patch > tmsg._body._init._patch) { iam._body._init._patch = tmsg._body._init._patch; } #endif param->_seq_num_res = iam._body._init._seq_num_res; param->_req_id_res = iam._body._init._req_id_res; param->_batch_size = iam._body._init._batch_size; param->_remote_zid = tmsg._body._init._zid; param->_remote_whatami = tmsg._body._init._whatami; param->_key_id_res = 0x08 << param->_key_id_res; param->_req_id_res = 0x08 << param->_req_id_res; _z_t_msg_clear(&tmsg); // Send InitAck _Z_DEBUG("Sending Z_INIT(Ack)"); ret = _z_link_send_t_msg(zl, &iam, socket); _z_t_msg_clear(&iam); if (ret != _Z_RES_OK) { return ret; } // Read t message from link ret = _z_link_recv_t_msg(&tmsg, zl, socket, recv_deadline); if (ret != _Z_RES_OK) { return ret; } // Receive OpenSyn if (_Z_MID(tmsg._header) != _Z_MID_T_OPEN || _Z_HAS_FLAG(tmsg._header, _Z_FLAG_T_INIT_A)) { _z_t_msg_clear(&tmsg); _Z_ERROR_RETURN(_Z_ERR_MESSAGE_UNEXPECTED); } _Z_DEBUG("Received Z_OPEN(Syn)"); // Process message param->_lease = (tmsg._body._open._lease < Z_TRANSPORT_LEASE) ? tmsg._body._open._lease : Z_TRANSPORT_LEASE; param->_initial_sn_rx = tmsg._body._open._initial_sn; _z_t_msg_clear(&tmsg); // Encode OpenAck _z_zint_t lease = Z_TRANSPORT_LEASE; _z_zint_t initial_sn = param->_initial_sn_tx; _z_transport_message_t oam = _z_t_msg_make_open_ack(lease, initial_sn); // Encode and send the message _Z_DEBUG("Sending Z_OPEN(Ack)"); ret = _z_link_send_t_msg(zl, &oam, socket); _z_t_msg_clear(&oam); if (ret != _Z_RES_OK) { return ret; } // Handshake finished return _Z_RES_OK; } z_result_t _z_unicast_open_client(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { return _z_unicast_handshake_open(param, zl, local_zid, Z_WHATAMI_CLIENT, NULL); } z_result_t _z_unicast_open_peer(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, int peer_op, _z_sys_net_socket_t *socket) { z_result_t ret = _Z_RES_OK; // Init sn tx z_random_fill(¶m->_initial_sn_tx, sizeof(param->_initial_sn_tx)); param->_initial_sn_tx = param->_initial_sn_tx & !_z_sn_modulo_mask(param->_seq_num_res); if (peer_op == _Z_PEER_OP_OPEN) { ret = _z_unicast_handshake_open(param, zl, local_zid, Z_WHATAMI_PEER, socket); } else { // Initialize common parameters param->_lease = Z_TRANSPORT_LEASE; param->_batch_size = Z_BATCH_UNICAST_SIZE; param->_seq_num_res = Z_SN_RESOLUTION; } return ret; } z_result_t _z_unicast_send_close(_z_transport_unicast_t *ztu, uint8_t reason, bool link_only) { z_result_t ret = _Z_RES_OK; // Send and clear message _z_transport_message_t cm = _z_t_msg_make_close(reason, link_only); ret = _z_transport_tx_send_t_msg(&ztu->_common, &cm, NULL); _z_t_msg_clear(&cm); return ret; } z_result_t _z_unicast_transport_close(_z_transport_unicast_t *ztu, uint8_t reason) { return _z_unicast_send_close(ztu, reason, false); } void _z_unicast_transport_clear(_z_transport_unicast_t *ztu) { _z_transport_peer_unicast_slist_free(&ztu->_peers); _z_pending_peers_clear(&ztu->_pending_peers); _z_transport_common_clear( &ztu->_common); // free common in the very end, as peers might access the link data in common while being freed } #else z_result_t _z_unicast_transport_create(_z_transport_t *zt, _z_link_t *zl, _z_transport_unicast_establish_param_t *param) { _ZP_UNUSED(zt); _ZP_UNUSED(zl); _ZP_UNUSED(param); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_unicast_open_client(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid) { _ZP_UNUSED(param); _ZP_UNUSED(zl); _ZP_UNUSED(local_zid); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_unicast_open_peer(_z_transport_unicast_establish_param_t *param, const _z_link_t *zl, const _z_id_t *local_zid, int peer_op, _z_sys_net_socket_t *socket) { _ZP_UNUSED(param); _ZP_UNUSED(zl); _ZP_UNUSED(local_zid); _ZP_UNUSED(peer_op); _ZP_UNUSED(socket); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_unicast_send_close(_z_transport_unicast_t *ztu, uint8_t reason, bool link_only) { _ZP_UNUSED(ztu); _ZP_UNUSED(reason); _ZP_UNUSED(link_only); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } z_result_t _z_unicast_transport_close(_z_transport_unicast_t *ztu, uint8_t reason) { _ZP_UNUSED(ztu); _ZP_UNUSED(reason); _Z_ERROR_RETURN(_Z_ERR_TRANSPORT_NOT_AVAILABLE); } void _z_unicast_transport_clear(_z_transport_unicast_t *ztu) { _ZP_UNUSED(ztu); } #endif // Z_FEATURE_UNICAST_TRANSPORT == 1 ================================================ FILE: src/transport/unicast.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico/transport/unicast.h" #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/link/link.h" #include "zenoh-pico/transport/common/rx.h" #include "zenoh-pico/transport/common/tx.h" #include "zenoh-pico/transport/multicast/rx.h" #include "zenoh-pico/transport/unicast/lease.h" #include "zenoh-pico/transport/unicast/read.h" #include "zenoh-pico/transport/unicast/rx.h" #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/uuid.h" #if Z_FEATURE_UNICAST_TRANSPORT == 1 void _zp_unicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback) { void *ctx = callback->context; _z_transport_peer_unicast_slist_t *l = zt->_transport._unicast._peers; for (; l != NULL; l = _z_transport_peer_unicast_slist_next(l)) { _z_transport_peer_unicast_t *val = _z_transport_peer_unicast_slist_value(l); z_id_t id = val->common._remote_zid; callback->call(&id, ctx); } } void _zp_unicast_info_session(const _z_transport_t *zt, _z_config_t *ps, int mode) { uint_fast8_t config_entry = (mode == Z_WHATAMI_CLIENT) ? Z_INFO_ROUTER_PID_KEY : Z_INFO_PEER_PID_KEY; _z_transport_peer_unicast_slist_t *xs = zt->_transport._unicast._peers; while (xs != NULL) { _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(xs); _z_string_t remote_zid_str = _z_id_to_string(&peer->common._remote_zid); _zp_config_insert_string(ps, config_entry, &remote_zid_str); _z_string_clear(&remote_zid_str); xs = _z_transport_peer_unicast_slist_next(xs); } } #else void _zp_unicast_fetch_zid(const _z_transport_t *zt, _z_closure_zid_t *callback) { _ZP_UNUSED(zt); _ZP_UNUSED(callback); } void _zp_unicast_info_session(const _z_transport_t *zt, _z_config_t *ps, int mode) { _ZP_UNUSED(zt); _ZP_UNUSED(ps); _ZP_UNUSED(mode); } #endif // Z_FEATURE_UNICAST_TRANSPORT == 1 ================================================ FILE: src/transport/utils.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/transport/utils.h" #include "zenoh-pico/protocol/core.h" #define U8_MAX 0xFF #define U16_MAX 0xFFFF #define U32_MAX 0xFFFFFFFF #define U64_MAX 0xFFFFFFFFFFFFFFFF _z_zint_t _z_sn_max(uint8_t bits) { _z_zint_t ret = 0; switch (bits) { case 0x00: { ret = U8_MAX >> 1; } break; case 0x01: { ret = U16_MAX >> 2; } break; case 0x02: { ret = U32_MAX >> 4; } break; case 0x03: { ret = (_z_zint_t)(U64_MAX >> 1); } break; default: { // Do nothing } break; } return ret; } _z_zint_t _z_sn_half(_z_zint_t sn) { return sn >> 1; } _z_zint_t _z_sn_modulo_mask(uint8_t bits) { _z_zint_t ret = 0; switch (bits) { case 0x00: { ret = U8_MAX >> 1; } break; case 0x01: { ret = U16_MAX >> 2; } break; case 0x02: { ret = U32_MAX >> 4; } break; case 0x03: { ret = (_z_zint_t)(U64_MAX >> 1); } break; default: { // Do nothing } break; } return ret; } bool _z_sn_precedes(const _z_zint_t sn_resolution, const _z_zint_t sn_left, const _z_zint_t sn_right) { _z_zint_t distance = (sn_right - sn_left) & sn_resolution; return ((distance <= _z_sn_half(sn_resolution)) && (distance != 0)); } bool _z_sn_consecutive(const _z_zint_t sn_resolution, const _z_zint_t sn_left, const _z_zint_t sn_right) { _z_zint_t distance = (sn_right - sn_left) & sn_resolution; return distance == 1; } _z_zint_t _z_sn_increment(const _z_zint_t sn_resolution, const _z_zint_t sn) { _z_zint_t ret = sn + 1; return (ret &= sn_resolution); } _z_zint_t _z_sn_decrement(const _z_zint_t sn_resolution, const _z_zint_t sn) { _z_zint_t ret = sn - 1; return (ret &= sn_resolution); } void _z_conduit_sn_list_copy(_z_conduit_sn_list_t *dst, const _z_conduit_sn_list_t *src) { dst->_is_qos = src->_is_qos; if (dst->_is_qos == false) { dst->_val._plain._best_effort = src->_val._plain._best_effort; dst->_val._plain._reliable = src->_val._plain._reliable; } else { for (uint8_t i = 0; i < Z_PRIORITIES_NUM; i++) { dst->_val._qos[i]._best_effort = src->_val._qos[i]._best_effort; dst->_val._qos[i]._reliable = src->_val._qos[i]._reliable; } } } void _z_conduit_sn_list_decrement(const _z_zint_t sn_resolution, _z_conduit_sn_list_t *sns) { if (sns->_is_qos == false) { sns->_val._plain._best_effort = _z_sn_decrement(sn_resolution, sns->_val._plain._best_effort); sns->_val._plain._reliable = _z_sn_decrement(sn_resolution, sns->_val._plain._reliable); } else { for (uint8_t i = 0; i < Z_PRIORITIES_NUM; i++) { sns->_val._qos[i]._best_effort = _z_sn_decrement(sn_resolution, sns->_val._qos[i]._best_effort); sns->_val._qos[i]._best_effort = _z_sn_decrement(sn_resolution, sns->_val._qos[i]._reliable); } } } ================================================ FILE: src/utils/checksum.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/checksum.h" #define __CRC_POLYNOMIAL 0x04C11DB7 uint32_t _z_crc32(const uint8_t *message, size_t len) { uint32_t crc = 0xFFFFFFFF; for (size_t i = 0; i < len; i++) { crc = crc ^ (uint32_t)message[i]; for (uint8_t j = 0; j < (uint8_t)8; j++) { crc = (crc >> 1) ^ ((uint32_t)__CRC_POLYNOMIAL & (uint32_t)(-(int32_t)(crc & (uint32_t)1))); } } return ~crc; } ================================================ FILE: src/utils/encoding.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/encoding.h" #include "zenoh-pico/utils/pointers.h" size_t _z_cobs_encode(const uint8_t *input, size_t input_len, uint8_t *output) { size_t len = input_len; uint8_t *pos = output; uint8_t *codep = output; uint8_t code = 1; pos = _z_ptr_u8_offset(pos, 1); for (const uint8_t *byte = input; len != (size_t)0; byte++) { if (*byte != 0x00) { *pos = *byte; pos = _z_ptr_u8_offset(pos, 1); code = code + (uint8_t)1; } if (!*byte || (code == (uint8_t)0xFF)) { *codep = code; code = 1; codep = pos; if (!*byte || len) { pos = _z_ptr_u8_offset(pos, 1); } } len = len - (size_t)1; } *codep = code; return _z_ptr_u8_diff(pos, output); } size_t _z_cobs_decode(const uint8_t *input, size_t input_len, uint8_t *output) { const uint8_t *byte = input; uint8_t *pos = output; uint8_t code = (uint8_t)0xFF; const uint8_t *input_end_ptr = _z_cptr_u8_offset(input, (ptrdiff_t)input_len); for (uint8_t block = (uint8_t)0x00; byte < input_end_ptr; block--) { if (block != (uint8_t)0x00) { *pos = *byte; pos = _z_ptr_u8_offset(pos, 1); byte = _z_cptr_u8_offset(byte, 1); } else { if (code != (uint8_t)0xFF) { *pos = (uint8_t)0x00; pos = _z_ptr_u8_offset(pos, 1); } code = *byte; block = *byte; byte = _z_cptr_u8_offset(byte, 1); if (code == (uint8_t)0x00) { pos = _z_ptr_u8_offset(pos, -1); break; } } } return _z_ptr_u8_diff(pos, output); } ================================================ FILE: src/utils/json_encoder.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/json_encoder.h" #include #include #include #include #include "zenoh-pico/api/primitives.h" #if Z_FEATURE_ADMIN_SPACE == 1 z_result_t _z_json_encoder_empty(_z_json_encoder_t *je) { if (je == NULL) { return _Z_ERR_INVALID; } *je = (_z_json_encoder_t){0}; _Z_RETURN_IF_ERR(z_bytes_writer_empty(&je->_bw)); return _Z_RES_OK; } static bool _z_json_encoder_value_allowed(_z_json_encoder_t *je) { if (je->_depth == 0) { return je->_done == false; } _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; if (frame->kind == _Z_JSON_CTX_OBJ && !frame->expect_value) { return false; } return true; } static inline z_result_t _z_json_encoder_putc(_z_json_encoder_t *je, char c) { const uint8_t b = (uint8_t)c; return z_bytes_writer_write_all(z_bytes_writer_loan_mut(&je->_bw), &b, 1); } static inline z_result_t _z_json_encoder_putsn(_z_json_encoder_t *je, const char *s, size_t len) { return z_bytes_writer_write_all(z_bytes_writer_loan_mut(&je->_bw), (const uint8_t *)s, len); } static z_result_t _z_json_encoder_push(_z_json_encoder_t *je, _z_json_ctx_kind_t kind) { if (je->_depth >= Z_JSON_MAX_DEPTH) { return _Z_ERR_OVERFLOW; } je->_stk[je->_depth].kind = kind; je->_stk[je->_depth].expect_value = false; je->_stk[je->_depth].first = true; je->_depth++; return _Z_RES_OK; } static z_result_t _z_json_encoder_pop(_z_json_encoder_t *je) { if (je->_depth == 0) { return _Z_ERR_UNDERFLOW; } je->_depth--; return _Z_RES_OK; } static z_result_t _z_json_encoder_finish_value(_z_json_encoder_t *je) { if (je->_depth == 0) { je->_done = true; return _Z_RES_OK; } _z_json_frame_t *parent = &je->_stk[je->_depth - 1]; if (parent->kind == _Z_JSON_CTX_OBJ) { parent->expect_value = false; } return _Z_RES_OK; } static z_result_t _z_json_encoder_before_key(_z_json_encoder_t *je) { if (je->_depth == 0) { return _Z_ERR_INVALID; } _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; if (frame->kind != _Z_JSON_CTX_OBJ || frame->expect_value) { return _Z_ERR_INVALID; } if (!frame->first) { _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, ',')); } else { frame->first = false; } return _Z_RES_OK; } static z_result_t _z_json_encoder_before_value(_z_json_encoder_t *je) { if (je->_depth == 0) { // root value: no separator return _Z_RES_OK; } _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; if (frame->kind == _Z_JSON_CTX_ARR) { if (!frame->first) { _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, ',')); } else { frame->first = false; } return _Z_RES_OK; } // object: value separators are handled at key time, and we must be expecting a value now if (frame->kind == _Z_JSON_CTX_OBJ) { if (!frame->expect_value) { return _Z_ERR_INVALID; } return _Z_RES_OK; } return _Z_ERR_INVALID; } z_result_t _z_json_encoder_start_object(_z_json_encoder_t *je) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); _Z_RETURN_IF_ERR(_z_json_encoder_push(je, _Z_JSON_CTX_OBJ)); z_result_t res = _z_json_encoder_putc(je, '{'); if (res != _Z_RES_OK) { _z_json_encoder_pop(je); } return res; } z_result_t _z_json_encoder_end_object(_z_json_encoder_t *je) { if (je == NULL || je->_depth == 0) { return _Z_ERR_INVALID; } _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; if (frame->kind != _Z_JSON_CTX_OBJ || frame->expect_value) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, '}')); _Z_RETURN_IF_ERR(_z_json_encoder_pop(je)); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_start_array(_z_json_encoder_t *je) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); _Z_RETURN_IF_ERR(_z_json_encoder_push(je, _Z_JSON_CTX_ARR)); z_result_t res = _z_json_encoder_putc(je, '['); if (res != _Z_RES_OK) { _z_json_encoder_pop(je); } return res; } z_result_t _z_json_encoder_end_array(_z_json_encoder_t *je) { if (je == NULL || je->_depth == 0) { return _Z_ERR_INVALID; } _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; if (frame->kind != _Z_JSON_CTX_ARR) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, ']')); _Z_RETURN_IF_ERR(_z_json_encoder_pop(je)); return _z_json_encoder_finish_value(je); } static z_result_t _z_json_encoder_write_escaped_string(_z_json_encoder_t *je, const char *str, size_t len) { _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, '"')); for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)str[i]; switch (c) { case '"': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\\"", 2)); break; case '\\': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\\\", 2)); break; case '\b': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\b", 2)); break; case '\f': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\f", 2)); break; case '\n': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\n", 2)); break; case '\r': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\r", 2)); break; case '\t': _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "\\t", 2)); break; default: if (c < 0x20) { // Encode other control chars as \u00XX char buf[7]; int n = snprintf(buf, sizeof(buf), "\\u%04x", (unsigned)c); if (n != 6) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, buf, 6)); } else { _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, (char)c)); } break; } } _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, '"')); return _Z_RES_OK; } z_result_t _z_json_encoder_write_key(_z_json_encoder_t *je, const char *key) { if (je == NULL || key == NULL || je->_depth == 0) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_key(je)); // Flawfinder: ignore [CWE-126] - Intended to take a null terminated string _Z_RETURN_IF_ERR(_z_json_encoder_write_escaped_string(je, key, strlen(key))); _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, ':')); _z_json_frame_t *frame = &je->_stk[je->_depth - 1]; frame->expect_value = true; return _Z_RES_OK; } z_result_t _z_json_encoder_write_string(_z_json_encoder_t *je, const char *value) { if (je == NULL || value == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); // Flawfinder: ignore [CWE-126] - Intended to take a null terminated string _Z_RETURN_IF_ERR(_z_json_encoder_write_escaped_string(je, value, strlen(value))); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_z_string(_z_json_encoder_t *je, const _z_string_t *value) { if (je == NULL || value == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); _Z_RETURN_IF_ERR(_z_json_encoder_write_escaped_string(je, _z_string_data(value), _z_string_len(value))); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_z_slice(_z_json_encoder_t *je, const _z_slice_t *value) { static const char hex[] = "0123456789abcdef"; if (je == NULL || value == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); const uint8_t *p = (const uint8_t *)value->start; size_t len = value->len; _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, '"')); for (size_t i = 0; i < len; i++) { uint8_t b = p[i]; char out[2] = {hex[b >> 4], hex[b & 0x0F]}; _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, out, 2)); } _Z_RETURN_IF_ERR(_z_json_encoder_putc(je, '"')); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_double(_z_json_encoder_t *je, double value) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } if (!isfinite(value)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); char buf[32]; int n = snprintf(buf, sizeof(buf), "%.17g", value); if (n <= 0 || (size_t)n >= sizeof(buf)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, buf, (size_t)n)); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_i64(_z_json_encoder_t *je, int64_t value) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); char buf[32]; // Flawfinder: ignore (CWE-134) - format string is compile-time constant (C99 PRId64), not attacker-controlled. int n = snprintf(buf, sizeof(buf), "%" PRId64, value); if (n <= 0 || (size_t)n >= sizeof(buf)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, buf, (size_t)n)); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_u64(_z_json_encoder_t *je, uint64_t value) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); char buf[32]; // Flawfinder: ignore (CWE-134) - format string is compile-time constant (C99 PRId64), not attacker-controlled. int n = snprintf(buf, sizeof(buf), "%" PRIu64, value); if (n <= 0 || (size_t)n >= sizeof(buf)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, buf, (size_t)n)); return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_boolean(_z_json_encoder_t *je, bool value) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); if (value) { _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "true", 4)); } else { _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "false", 5)); } return _z_json_encoder_finish_value(je); } z_result_t _z_json_encoder_write_null(_z_json_encoder_t *je) { if (je == NULL || !_z_json_encoder_value_allowed(je)) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_before_value(je)); _Z_RETURN_IF_ERR(_z_json_encoder_putsn(je, "null", 4)); return _z_json_encoder_finish_value(je); } static z_result_t _z_json_encoder_validate(const _z_json_encoder_t *je) { if (je == NULL || je->_depth != 0 || !je->_done) { return _Z_ERR_INVALID; } return _Z_RES_OK; } z_result_t _z_json_encoder_finish(_z_json_encoder_t *je, z_owned_bytes_t *bytes) { if (je == NULL || bytes == NULL) { return _Z_ERR_INVALID; } _Z_RETURN_IF_ERR(_z_json_encoder_validate(je)); z_bytes_writer_finish(z_bytes_writer_move(&je->_bw), bytes); je->_depth = 0; je->_done = false; return _Z_RES_OK; } void _z_json_encoder_clear(_z_json_encoder_t *je) { if (je == NULL) { return; } z_bytes_writer_drop(z_bytes_writer_move(&je->_bw)); je->_depth = 0; je->_done = false; } #endif // Z_FEATURE_ADMIN_SPACE == 1 ================================================ FILE: src/utils/pointers.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/pointers.h" const uint8_t *_z_cptr_u8_offset(const uint8_t *ptr, const ptrdiff_t off) { return ptr + off; } const char *_z_cptr_char_offset(const char *ptr, const ptrdiff_t off) { return ptr + off; } ================================================ FILE: src/utils/query_params.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/pointers.h" _z_query_param_t _z_query_params_next(_z_str_se_t *str) { _z_query_param_t result = {0}; _z_splitstr_t params = {.s = *str, .delimiter = _Z_QUERY_PARAMS_LIST_SEPARATOR}; _z_str_se_t param = _z_splitstr_next(¶ms); str->start = params.s.start; str->end = params.s.end; if (param.start != NULL) { _z_splitstr_t kvpair = {.s = param, .delimiter = _Z_QUERY_PARAMS_FIELD_SEPARATOR}; _z_str_se_t key = _z_splitstr_next(&kvpair); // Set key if length > 0 if (_z_ptr_char_diff(key.end, key.start) > 0) { result.key.start = key.start; result.key.end = key.end; // Set value if length > 0 if (_z_ptr_char_diff(kvpair.s.end, kvpair.s.start) > 0) { result.value.start = kvpair.s.start; result.value.end = kvpair.s.end; } } } return result; } bool _z_parameters_has_anyke(const char *parameters, size_t parameters_len) { if (parameters == NULL || parameters_len == 0) { return false; } const char *start = (const char *)parameters; const char *end = start + parameters_len; while (true) { size_t len = (size_t)(end - start); const char *pos = _z_memmem(start, len, _Z_QUERY_PARAMS_KEY_ANYKE, _Z_QUERY_PARAMS_KEY_ANYKE_LEN); if (pos == NULL) { break; } // Check left boundary: must be start of string or preceded by ';' bool left_ok = (pos == parameters) || (*(pos - 1) == _Z_QUERY_PARAMS_LIST_SEPARATOR[0]); // Check right boundary: must be end of string or followed by ';' bool right_ok = (pos + _Z_QUERY_PARAMS_KEY_ANYKE_LEN == end) || (*(pos + _Z_QUERY_PARAMS_KEY_ANYKE_LEN) == _Z_QUERY_PARAMS_LIST_SEPARATOR[0]); if (left_ok && right_ok) { return true; } start = pos + _Z_QUERY_PARAMS_KEY_ANYKE_LEN + 1; if (start > end) break; } return false; } ================================================ FILE: src/utils/string.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/string.h" #include #include #include "zenoh-pico/utils/pointers.h" _z_str_se_t _z_bstrnew(const char *start) { return (_z_str_se_t){.start = start, .end = strchr(start, 0)}; } char const *_z_rstrstr(char const *haystack_start, char const *haystack_end, const char *needle_start) { char const *hs = haystack_start; char const *he = haystack_end; const char *needle_end = strchr(needle_start, 0); hs = _z_cptr_char_offset(hs, (ptrdiff_t)_z_ptr_char_diff(needle_end, needle_start)); char const *result = NULL; while ((result == false) && (he >= hs)) { char found = 1; char const *needle = _z_cptr_char_offset(needle_end, -1); char const *haystack = _z_cptr_char_offset(he, -1); while (needle >= needle_start) { if (needle[0] != haystack[0]) { found = 0; break; } needle = _z_cptr_char_offset(needle, -1); haystack = _z_cptr_char_offset(haystack, -1); } if (found == true) { result = he; } he = _z_cptr_char_offset(he, -1); } return result; } char const *_z_bstrstr(_z_str_se_t haystack, _z_str_se_t needle) { haystack.end = _z_cptr_char_offset(haystack.end, -1 * (ptrdiff_t)_z_ptr_char_diff(needle.end, needle.start)); char const *result = NULL; for (; (result == false) && (haystack.start <= haystack.end); haystack.start = _z_cptr_char_offset(haystack.start, 1)) { bool found = true; char const *n = needle.start; char const *h = haystack.start; while (_z_ptr_char_diff(needle.end, n) > 0) { if (n[0] != h[0]) { found = false; break; } n = _z_cptr_char_offset(n, 1); h = _z_cptr_char_offset(h, 1); } if (found == true) { result = haystack.start; } } return result; } char const *_z_strstr(char const *haystack_start, char const *haystack_end, const char *needle_start) { const char *needle_end = strchr(needle_start, 0); return _z_bstrstr((_z_str_se_t){.start = haystack_start, .end = haystack_end}, (_z_str_se_t){.start = needle_start, .end = needle_end}); } char const *_z_strstr_skipneedle(char const *haystack_start, char const *haystack_end, const char *needle_start) { const char *needle_end = strchr(needle_start, 0); return _z_bstrstr_skipneedle((_z_str_se_t){.start = haystack_start, .end = haystack_end}, (_z_str_se_t){.start = needle_start, .end = needle_end}); } char const *_z_bstrstr_skipneedle(_z_str_se_t haystack, _z_str_se_t needle) { char const *result = _z_bstrstr(haystack, needle); if (result != NULL) { result = _z_cptr_char_offset(result, (ptrdiff_t)_z_ptr_char_diff(needle.end, needle.start)); } return result; } bool _z_splitstr_is_empty(const _z_splitstr_t *src) { return src->s.start == NULL; } _z_str_se_t _z_splitstr_next(_z_splitstr_t *str) { _z_str_se_t result = str->s; if (str->s.start != NULL) { result.end = _z_strstr(result.start, result.end, str->delimiter); if (result.end == NULL) { result.end = str->s.end; } if (result.end == str->s.end) { str->s = (_z_str_se_t){.start = NULL, .end = NULL}; } else { str->s.start = _z_cptr_char_offset(result.end, (ptrdiff_t)strlen(str->delimiter)); } } return result; } _z_str_se_t _z_splitstr_split_once(_z_splitstr_t src, _z_str_se_t *next) { *next = _z_splitstr_next(&src); return src.s; } _z_str_se_t _z_splitstr_nextback(_z_splitstr_t *str) { _z_str_se_t result = str->s; if (str->s.start != NULL) { result.start = _z_rstrstr(result.start, result.end, str->delimiter); if (result.start == NULL) { result.start = str->s.start; } if (result.start == str->s.start) { str->s = (_z_str_se_t){.start = NULL, .end = NULL}; } else { str->s.end = _z_cptr_char_offset(result.start, -1 * (ptrdiff_t)strlen(str->delimiter)); } } return result; } size_t _z_strcnt(char const *haystack_start, const char *harstack_end, const char *needle_start) { char const *hs = haystack_start; size_t result = 0; while (hs != NULL) { result = result + (size_t)1; hs = _z_strstr_skipneedle(hs, harstack_end, needle_start); } return result; } size_t _z_str_startswith(const char *s, const char *needle) { size_t i = 0; for (; needle[i] != '\0'; i++) { if (s[i] != needle[i]) { i = 0; break; } } return i; } bool _z_str_se_atoui(const _z_str_se_t *str, uint32_t *result) { uint32_t value = 0; size_t len = _z_ptr_char_diff(str->end, str->start); if (len == 0 || len > 10) { return false; } const uint32_t threshold = UINT32_MAX / 10; const uint32_t rem_threshold = UINT32_MAX % 10; for (size_t i = 0; i < len; i++) { const char c = str->start[i]; if (c < '0' || c > '9') { return false; } uint32_t digit = (uint32_t)(c - '0'); if (value > threshold || (value == threshold && digit > rem_threshold)) { return false; } value = value * 10 + digit; } *result = value; return true; } bool _z_str_parse_i32(const char *s, int32_t *out) { bool is_negative = false; uint32_t value = 0; const uint32_t limit = ((uint32_t)INT32_MAX) + 1u; if (s == NULL || *s == '\0') { return false; } if (*s == '-' || *s == '+') { is_negative = *s == '-'; s++; } if (*s == '\0') { return false; } uint32_t max_value = is_negative ? limit : (uint32_t)INT32_MAX; while (*s != '\0') { if (*s < '0' || *s > '9') { return false; } uint32_t digit = (uint32_t)(*s - '0'); if (value > ((max_value - digit) / 10u)) { return false; } value = (value * 10u) + digit; s++; } if (is_negative) { *out = (value == limit) ? INT32_MIN : -(int32_t)value; } else { *out = (int32_t)value; } return true; } bool _z_str_parse_bool(const char *s, bool *out) { if (s == NULL) { return false; } if (strcmp(s, "true") == 0) { *out = true; return true; } if (strcmp(s, "false") == 0) { *out = false; return true; } return false; } void *_z_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) { if (haystack == NULL || needle == NULL || haystack_len < needle_len) { return NULL; } const uint8_t *h = haystack; const uint8_t *h_end = h + haystack_len; const uint8_t *n = needle; while (h <= h_end - needle_len) { if (memcmp(h, n, needle_len) == 0) { return (void *)h; } h++; } return NULL; } ================================================ FILE: src/utils/time_range.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/time_range.h" #include #include #include #include "zenoh-pico/utils/logging.h" #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/string.h" /** * Parses a _z_str_se_t as a duration. * Expected format is a double in seconds, or "" where is: * - 'u' => microseconds * - "ms" => milliseconds * - 's' => seconds * - 'm' => minutes * - 'h' => hours * - 'd' => days * - 'w' => weeks * * Returns true if the duration is successfully parsed, false otherwise. * If true, duration will contain the parsed duration on return. */ static bool _z_time_range_parse_duration(const _z_str_se_t *bound, double *duration) { size_t len = _z_ptr_char_diff(bound->end, bound->start); if (len == 0) { return false; } double multiplier = 1.0; switch (*_z_cptr_char_offset(bound->end, -1)) { case 'u': multiplier = _Z_TIME_RANGE_U_TO_SECS; len--; break; case 's': if (len > 1 && *_z_cptr_char_offset(bound->end, -2) == 'm') { multiplier = _Z_TIME_RANGE_MS_TO_SECS; len--; } len--; break; case 'm': multiplier = _Z_TIME_RANGE_M_TO_SECS; len--; break; case 'h': multiplier = _Z_TIME_RANGE_H_TO_SECS; len--; break; case 'd': multiplier = _Z_TIME_RANGE_D_TO_SECS; len--; break; case 'w': multiplier = _Z_TIME_RANGE_W_TO_SECS; len--; break; } // String contains unit only if (len == 0) { return false; } char *buf = z_malloc(len + 1); if (buf == NULL) { _Z_ERROR("Failed to allocate buffer."); return false; } size_t offset = 0; if (!_z_memcpy_checked(buf, len + 1, &offset, bound->start, len)) { z_free(buf); return false; } buf[len] = '\0'; char *err; double value = strtod(bound->start, &err); z_free(buf); if (value == 0 && *err != '\0') { return false; } value *= multiplier; // Check for overflow if (isinf(value)) { return false; } *duration = value; return true; } static bool _z_time_range_parse_time_bound(_z_str_se_t *str, bool inclusive, _z_time_bound_t *bound) { size_t len = _z_ptr_char_diff(str->end, str->start); if (len == 0) { bound->bound = _Z_TIME_BOUND_UNBOUNDED; return true; } else if (len >= 5) { bound->bound = inclusive ? _Z_TIME_BOUND_INCLUSIVE : _Z_TIME_BOUND_EXCLUSIVE; if (*str->start == 'n' && *_z_cptr_char_offset(str->start, 1) == 'o' && *_z_cptr_char_offset(str->start, 2) == 'w' && *_z_cptr_char_offset(str->start, 3) == '(' && *_z_cptr_char_offset(str->end, -1) == ')') { if (len == 5) { bound->now_offset = 0; } else { _z_str_se_t duration = {.start = _z_cptr_char_offset(str->start, 4), .end = _z_cptr_char_offset(str->end, -1)}; if (!_z_time_range_parse_duration(&duration, &bound->now_offset)) { return false; } } return true; } // TODO: Add support for RFC3339 timestamps } return false; } bool _z_time_range_from_str(const char *str, size_t len, _z_time_range_t *range) { // Minimum length is 4 "[..]" if (len < 4) { return false; } bool inclusive_start; switch (str[0]) { case '[': inclusive_start = true; break; case ']': inclusive_start = false; break; default: return false; } bool inclusive_end; switch (str[len - 1]) { case '[': inclusive_end = false; break; case ']': inclusive_end = true; break; default: return false; } // Search for '..' const char *separator = _z_strstr(_z_cptr_char_offset(str, 1), _z_cptr_char_offset(str, (ptrdiff_t)(len - 1)), ".."); if (separator != NULL) { _z_str_se_t start_str = {.start = _z_cptr_char_offset(str, 1), .end = separator}; _z_str_se_t end_str = {.start = _z_cptr_char_offset(separator, 2), .end = _z_cptr_char_offset(str, (ptrdiff_t)(len - 1))}; if (!_z_time_range_parse_time_bound(&start_str, inclusive_start, &range->start) || !_z_time_range_parse_time_bound(&end_str, inclusive_end, &range->end)) { return false; } } else { // Search for ';' separator = _z_strstr(_z_cptr_char_offset(str, 1), _z_cptr_char_offset(str, (ptrdiff_t)(len - 1)), ";"); if (separator == NULL) { return false; } _z_str_se_t start_str = {.start = _z_cptr_char_offset(str, 1), .end = separator}; _z_str_se_t end_str = {.start = _z_cptr_char_offset(separator, 1), .end = _z_cptr_char_offset(str, (ptrdiff_t)(len - 1))}; if (!_z_time_range_parse_time_bound(&start_str, inclusive_start, &range->start) || range->start.bound == _Z_TIME_BOUND_UNBOUNDED) { return false; } double duration; if (!_z_time_range_parse_duration(&end_str, &duration)) { return false; } range->end.bound = inclusive_end ? _Z_TIME_BOUND_INCLUSIVE : _Z_TIME_BOUND_EXCLUSIVE; range->end.now_offset = range->start.now_offset + duration; } return true; } static bool _z_time_bound_delim_to_char(const _z_time_bound_t *bound, bool is_start, char *delim) { if (bound == NULL || delim == NULL) { return false; } switch (bound->bound) { case _Z_TIME_BOUND_INCLUSIVE: *delim = is_start ? '[' : ']'; break; case _Z_TIME_BOUND_EXCLUSIVE: *delim = is_start ? ']' : '['; break; case _Z_TIME_BOUND_UNBOUNDED: *delim = is_start ? '[' : ']'; break; default: return false; } return true; } bool _z_time_bound_to_str(const _z_time_bound_t *bound, char *buf, size_t buf_len) { if (bound == NULL || buf == NULL || buf_len < 1) { return false; // Not enough space for at least '\0' } if (bound->bound == _Z_TIME_BOUND_UNBOUNDED) { buf[0] = '\0'; return true; } size_t pos = 0; // Ensure enough space for "now(" if (buf_len < 4) { return false; } buf[pos++] = 'n'; buf[pos++] = 'o'; buf[pos++] = 'w'; buf[pos++] = '('; if (bound->now_offset != 0.0) { int len = snprintf(&buf[pos], buf_len - pos, "%.17f", bound->now_offset); if (len < 0 || (size_t)len >= buf_len - pos) { return false; // Not enough space for the formatted string } // Trim trailing zeros and decimal point if no fractions remain char *start = &buf[pos]; char *dot = strchr(start, '.'); if (dot != NULL) { size_t remaining = buf_len - pos; size_t str_len = strnlen(start, remaining); if (str_len == remaining) { // Null terminator not found within bounds return false; } char *end = start + str_len - 1; while (end > dot && *end == '0') { *end-- = '\0'; } if (end == dot) { *end = '\0'; // remove trailing '.' } } size_t remaining = buf_len - pos; size_t str_len = strnlen(start, remaining); if (str_len == remaining) { // Null terminator not found within bounds return false; } pos += str_len; if (buf_len - pos < 1) { return false; // Not enough space for 's' } buf[pos++] = 's'; } if (buf_len - pos < 2) { return false; // Not enough space for ")\0" } buf[pos++] = ')'; buf[pos++] = '\0'; return true; } bool _z_time_range_to_str(const _z_time_range_t *range, char *buf, size_t buf_len) { size_t pos = 0; if (range == NULL || buf == NULL || buf_len < 3) { return false; // Not enough space for at least "[]" } if (!_z_time_bound_delim_to_char(&range->start, true, &buf[pos++])) { return false; // Invalid bound delimiter } if (range->start.bound == _Z_TIME_BOUND_INCLUSIVE || range->start.bound == _Z_TIME_BOUND_EXCLUSIVE) { if (!_z_time_bound_to_str(&range->start, &buf[pos], buf_len - pos)) { return false; } size_t remaining = buf_len - pos; size_t str_len = strnlen(&buf[pos], remaining); if (str_len == remaining) { // Null terminator not found within bounds return false; } pos += str_len; } if (buf_len - pos < 2) { return false; // Not enough space for ".." } buf[pos++] = '.'; buf[pos++] = '.'; if (range->end.bound == _Z_TIME_BOUND_INCLUSIVE || range->end.bound == _Z_TIME_BOUND_EXCLUSIVE) { if (!_z_time_bound_to_str(&range->end, &buf[pos], buf_len - pos)) { return false; } size_t remaining = buf_len - pos; size_t str_len = strnlen(&buf[pos], remaining); if (str_len == remaining) { // Null terminator not found within bounds return false; } pos += str_len; } if (buf_len - pos < 2) { return false; // Not enough space for end delimiter and '\0' } if (!_z_time_bound_delim_to_char(&range->end, false, &buf[pos++])) { return false; // Invalid bound delimiter } buf[pos] = '\0'; return true; } _z_ntp64_t _z_time_range_resolve_offset(_z_ntp64_t base, double offset) { const double FRAC_PER_SEC = 4294967296.0; // 2^32 if (offset == 0.0) { return base; } double abs_offset = fabs(offset); uint32_t offset_seconds = (uint32_t)abs_offset; double fractions = abs_offset - (double)offset_seconds; uint32_t offset_fractions = (uint32_t)(fractions * FRAC_PER_SEC + 0.5); _z_ntp64_t offset_ntp = ((uint64_t)offset_seconds << 32) | offset_fractions; if (offset < 0.0) { return base - offset_ntp; } else { return base + offset_ntp; } } bool _z_time_range_contains_at_time(const _z_time_range_t *range, const _z_ntp64_t timestamp, const _z_ntp64_t time) { if (range == NULL) { return false; } if (range->start.bound != _Z_TIME_BOUND_UNBOUNDED) { _z_ntp64_t start = _z_time_range_resolve_offset(time, range->start.now_offset); if (range->start.bound == _Z_TIME_BOUND_INCLUSIVE) { if (start > timestamp) { return false; } } else { // EXCLUSIVE if (start >= timestamp) { return false; } } } if (range->end.bound != _Z_TIME_BOUND_UNBOUNDED) { _z_ntp64_t end = _z_time_range_resolve_offset(time, range->end.now_offset); if (range->end.bound == _Z_TIME_BOUND_INCLUSIVE) { if (end < timestamp) { return false; } } else { // EXCLUSIVE if (end <= timestamp) { return false; } } } return true; } ================================================ FILE: src/utils/uuid.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/utils/uuid.h" #include #include #include #include "zenoh-pico/api/types.h" #include "zenoh-pico/utils/pointers.h" #define UUID_SIZE 16 void _z_uuid_to_bytes(uint8_t *bytes, const char *uuid_str) { uint8_t n_dash = 0; for (uint8_t i = 0; i < 32; i += 2) { if (i == 8 || i == 12 || i == 16 || i == 18) { n_dash += 1; } char val[5] = {'0', 'x', uuid_str[i + n_dash], uuid_str[i + 1 + n_dash], '\0'}; *bytes = (uint8_t)strtoul(val, NULL, 0); bytes = _z_ptr_u8_offset(bytes, 1); } } _z_string_t _z_id_to_string(const _z_id_t *id) { _z_slice_t buf = _z_slice_alias_buf(id->id, sizeof(id->id)); return _z_string_convert_bytes_le(&buf); } _z_id_t _z_id_from_string(const _z_string_t *str) { if (str == NULL) { return _z_id_empty(); } const char *s = _z_string_data(str); size_t len = _z_string_len(str); // Expect exactly ZENOH_ID_SIZE * 2 lowercase hex characters if (s == NULL || len != ZENOH_ID_SIZE * 2) { return _z_id_empty(); } z_id_t id; for (size_t i = 0; i < ZENOH_ID_SIZE; i++) { size_t offset = (ZENOH_ID_SIZE - 1 - i) * 2; char high = s[offset]; char low = s[offset + 1]; if (!isxdigit(high) || !isxdigit(low) || isupper((unsigned char)high) || isupper((unsigned char)low)) { return _z_id_empty(); } char byte_str[3] = {high, low, '\0'}; unsigned long byte_val = strtoul(byte_str, NULL, 16); id.id[i] = (uint8_t)byte_val; } return id; } ================================================ FILE: tests/api.sh ================================================ #!/bin/sh # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # TESTBIN="$1" TESTDIR=$(dirname "$0") if [ "$OSTYPE" = "msys" ]; then TESTBIN="$TESTDIR/Debug/$TESTBIN.exe" else TESTBIN="./$TESTBIN" fi cd "$TESTDIR"|| exit echo "------------------ Running test $TESTBIN -------------------" sleep 5 if [ ! -f zenohd ]; then git clone https://github.com/eclipse-zenoh/zenoh.git zenoh-git cd zenoh-git || exit if [ -n "$ZENOH_BRANCH" ]; then git switch "$ZENOH_BRANCH" fi rustup show cargo build --lib --bin zenohd cp ./target/debug/zenohd "$TESTDIR"/ cd "$TESTDIR"|| exit fi chmod +x zenohd LOCATORS="tcp/127.0.0.1:7447" for LOCATOR in $(echo "$LOCATORS" | xargs); do sleep 1 echo "> Running zenohd ... $LOCATOR" RUST_LOG=debug ./zenohd --plugin-search-dir "$TESTDIR/zenoh-git/target/debug" -l "$LOCATOR" > zenohd."$1".log 2>&1 & ZPID=$! sleep 5 echo "> Running $TESTBIN ..." "$TESTBIN" "$LOCATOR" RETCODE=$? echo "> Stopping zenohd ..." kill -9 "$ZPID" sleep 1 echo "> Logs of zenohd ..." cat zenohd."$1".log [ "$RETCODE" -lt 0 ] && exit "$RETCODE" done echo "> Done ($RETCODE)." exit "$RETCODE" ================================================ FILE: tests/attachment.py ================================================ import argparse import os from signal import SIGINT import subprocess import sys import time import re # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" def pub_and_sub(): print("*** Pub & sub test ***") test_status = 0 # Expected z_pub output & status z_pub_expected_status = 0 z_pub_expected_output = '''Opening session... Declaring publisher for 'demo/example/zenoh-pico-pub'... Press CTRL-C to quit... Putting Data ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!')...''' # Expected z_sub output & status z_sub_expected_status = 0 z_sub_expected_output = '''Opening session... Declaring Subscriber on 'demo/example/**'... Press CTRL-C to quit... >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!') with encoding: zenoh/string;utf8 with timestamp: with attachment: 0: source, C 1: index, 0 >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!') with encoding: zenoh/string;utf8 with timestamp: with attachment: 0: source, C 1: index, 1 >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!') with encoding: zenoh/string;utf8 with timestamp: with attachment: 0: source, C 1: index, 2 >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!') with encoding: zenoh/string;utf8 with timestamp: with attachment: 0: source, C 1: index, 3 >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!') with encoding: zenoh/string;utf8 with timestamp: with attachment: 0: source, C 1: index, 4''' print("Start subscriber") # Start z_sub in the background z_sub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_sub_attachment -n 5" z_sub_process = subprocess.Popen( z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Introduce a delay to ensure z_sub starts time.sleep(2) print("Start publisher") # Start z_pub z_pub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_pub_attachment -n 5" z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Wait for z_pub to finish z_pub_process.wait() print("Stop subscriber") time.sleep(2) if z_sub_process.poll() is None: # send SIGINT to group z_sub_process_gid = os.getpgid(z_sub_process.pid) os.killpg(z_sub_process_gid, SIGINT) # Wait for z_sub to finish z_sub_process.wait() print("Check publisher status & output") # Check the exit status of z_pub z_pub_status = z_pub_process.returncode if z_pub_status == z_pub_expected_status: print("z_pub status valid") else: print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") test_status = 1 # Check output of z_pub z_pub_output = z_pub_process.stdout.read() if z_pub_expected_output in z_pub_output: print("z_pub output valid") else: print("z_pub output invalid:") print(f"Expected: \"{z_pub_expected_output}\"") print(f"Received: \"{z_pub_output}\"") test_status = 1 print("Check subscriber status & output") # Check the exit status of z_sub z_sub_status = z_sub_process.returncode if z_sub_status == z_sub_expected_status: print("z_sub status valid") else: print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") test_status = 1 # Check output of z_sub z_sub_output = re.sub(r' with timestamp: \d+\n', ' with timestamp: \n', z_sub_process.stdout.read()) if z_sub_expected_output in z_sub_output: print("z_sub output valid") else: print("z_sub output invalid:") print(f"Expected: \"{z_sub_expected_output}\"") print(f"Received: \"{z_sub_output}\"") test_status = 1 # Return value return test_status def query_and_queryable(): print("*** Query & queryable test ***") test_status = 0 # Expected z_query output & status z_query_expected_status = 0 z_query_expected_output = """Opening session... Sending Query 'demo/example/**'... >> Received ('demo/example/**': 'Queryable from Pico!') with encoding: zenoh/string;utf8 with attachment: 0: reply_key, reply_value >> Received query final notification""" # Expected z_queryable output & status z_queryable_expected_status = 0 z_queryable_expected_output = """Opening session... Creating Queryable on 'demo/example/zenoh-pico-queryable'... Press CTRL-C to quit... >> [Queryable handler] Received Query 'demo/example/**' with encoding: zenoh/string;utf8 with attachment: 0: test_key, test_value""" print("Start queryable") # Start z_queryable in the background z_queryable_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_queryable_attachment -n 1" z_queryable_process = subprocess.Popen( z_queryable_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Introduce a delay to ensure z_queryable starts time.sleep(2) print("Start query") # Start z_query z_query_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_get_attachment" z_query_process = subprocess.Popen( z_query_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Wait for z_query to finish z_query_process.wait() print("Stop queryable") time.sleep(2) if z_queryable_process.poll() is None: # send SIGINT to group z_quaryable_process_gid = os.getpgid(z_queryable_process.pid) os.killpg(z_quaryable_process_gid, SIGINT) # Wait for z_queryable to finish z_queryable_process.wait() print("Check query status & output") # Check the exit status of z_query z_query_status = z_query_process.returncode if z_query_status == z_query_expected_status: print("z_query status valid") else: print(f"z_query status invalid, expected: {z_query_expected_status}," f" received: {z_query_status}") test_status = 1 # Check output of z_query z_query_output = z_query_process.stdout.read() if z_query_expected_output in z_query_output: print("z_query output valid") else: print("z_query output invalid:") print(f'Expected: "{z_query_expected_output}"') print(f'Received: "{z_query_output}"') test_status = 1 print("Check queryable status & output") # Check the exit status of z_queryable z_queryable_status = z_queryable_process.returncode if z_queryable_status == z_queryable_expected_status: print("z_queryable status valid") else: print(f"z_queryable status invalid, expected: {z_queryable_expected_status}," f" received: {z_queryable_status}") test_status = 1 # Check output of z_queryable z_queryable_output = z_queryable_process.stdout.read() if z_queryable_expected_output in z_queryable_output: print("z_queryable output valid") else: print("z_queryable output invalid:") print(f'Expected: "{z_queryable_expected_output}"') print(f'Received: "{z_queryable_output}"') test_status = 1 # Return status return test_status if __name__ == "__main__": EXIT_STATUS = 0 # Test pub and sub examples if pub_and_sub() == 1: EXIT_STATUS = 1 # Test query and queryable examples if query_and_queryable() == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/connection_restore.py ================================================ import subprocess import time import sys import os import threading ROUTER_INIT_TIMEOUT_S = 3 WAIT_MESSAGE_TIMEOUT_S = 15 DISCONNECT_MESSAGES = ["Closing session because it has expired", "Send keep alive failed"] CONNECT_MESSAGES = ["Z_OPEN(Ack)"] LIVELINESS_TOKEN_ALIVE_MESSAGES = ["[LivelinessSubscriber] New alive token"] LIVELINESS_TOKEN_DROP_MESSAGES = ["[LivelinessSubscriber] Dropped token"] SUBSCRIBER_RECEIVE_MESSAGES = ["[Subscriber] Received"] ROUTER_ERROR_MESSAGE = "ERROR" WRITE_FILTER_OFF_MESSAGE = "write filter state: 1" WRITE_FILTER_ACTIVE_MESSAGE = "write filter state: 0" ZENOH_PORT = "7447" ROUTER_ARGS = ['-l', f'tcp/0.0.0.0:{ZENOH_PORT}', '--no-multicast-scouting'] STDBUF_CMD = ["stdbuf", "-o0"] DIR_EXAMPLES = "build/examples" ACTIVE_CLIENT_COMMAND = STDBUF_CMD + [f'{DIR_EXAMPLES}/z_pub', '-e', f'tcp/127.0.0.1:{ZENOH_PORT}'] PASSIVE_CLIENT_COMMAND = STDBUF_CMD + [f'{DIR_EXAMPLES}/z_sub', '-e', f'tcp/127.0.0.1:{ZENOH_PORT}'] LIVELINESS_CLIENT_COMMAND = STDBUF_CMD + [f'{DIR_EXAMPLES}/z_liveliness', '-e', f'tcp/127.0.0.1:{ZENOH_PORT}'] LIVELINESS_SUB_CLIENT_COMMAND = STDBUF_CMD + [f'{DIR_EXAMPLES}/z_sub_liveliness', '-h', '-e', f'tcp/127.0.0.1:{ZENOH_PORT}'] LIBASAN_PATH = subprocess.run(["gcc", "-print-file-name=libasan.so"], stdout=subprocess.PIPE, text=True, check=True).stdout.strip() def run_process(command, output_collector, process_list): env = os.environ.copy() env["RUST_LOG"] = "trace" if LIBASAN_PATH: env["LD_PRELOAD"] = LIBASAN_PATH print(f"Run {command}") process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, env=env) process_list.append(process) for line in iter(process.stdout.readline, ''): print(f"-- [{process.pid}]:", line.strip()) output_collector.append(line.strip()) process.stdout.close() process.wait() def run_background(command, output_collector, process_list): thread = threading.Thread(target=run_process, args=(command, output_collector, process_list)) thread.start() def terminate_processes(process_list): for process in process_list: process.terminate() try: process.wait(timeout=5) except subprocess.TimeoutExpired: process.kill() process_list.clear() def block_connection(): subprocess.run(["iptables", "-A", "INPUT", "-p", "tcp", "--dport", ZENOH_PORT, "-j", "DROP"], check=True) subprocess.run(["iptables", "-A", "OUTPUT", "-p", "tcp", "--sport", ZENOH_PORT, "-j", "DROP"], check=True) def unblock_connection(): subprocess.run(["iptables", "-D", "INPUT", "-p", "tcp", "--dport", ZENOH_PORT, "-j", "DROP"], check=False) subprocess.run(["iptables", "-D", "OUTPUT", "-p", "tcp", "--sport", ZENOH_PORT, "-j", "DROP"], check=False) def wait_messages(client_output, messages): start_time = time.time() while time.time() - start_time < WAIT_MESSAGE_TIMEOUT_S: if any(message in line for line in client_output for message in messages): return True time.sleep(1) return False def wait_connect(client_output): if wait_messages(client_output, CONNECT_MESSAGES): print("Initial connection successful.") else: raise Exception("Connection failed.") def wait_reconnect(client_output): if wait_messages(client_output, CONNECT_MESSAGES): print("Connection restored successfully.") else: raise Exception("Failed to restore connection.") def wait_disconnect(client_output): if wait_messages(client_output, DISCONNECT_MESSAGES): print("Connection lost successfully.") else: raise Exception("Failed to block connection.") def check_router_errors(router_output): for line in router_output: if ROUTER_ERROR_MESSAGE in line: print(line) raise Exception("Router have an error.") def test_connection_drop(router_command, client_command, timeout): print(f"Drop test {client_command} for timeout {timeout}") router_output = [] client_output = [] process_list = [] blocked = False try: run_background(router_command, router_output, process_list) time.sleep(ROUTER_INIT_TIMEOUT_S) run_background(client_command, client_output, process_list) # Two iterations because there was an error on the second reconnection for _ in range(2): wait_connect(client_output) client_output.clear() print("Blocking connection...") block_connection() blocked = True time.sleep(timeout) wait_disconnect(client_output) client_output.clear() print("Unblocking connection...") unblock_connection() blocked = False wait_reconnect(client_output) check_router_errors(router_output) print(f"Drop test {client_command} for timeout {timeout} passed") finally: if blocked: unblock_connection() terminate_processes(process_list) def test_router_restart(router_command, client_command, timeout): print(f"Restart test {client_command} for timeout {timeout}") router_output = [] client_output = [] router_process_list = [] client_process_list = [] try: run_background(router_command, router_output, router_process_list) time.sleep(ROUTER_INIT_TIMEOUT_S) run_background(client_command, client_output, client_process_list) wait_connect(client_output) client_output.clear() print("Stop router...") terminate_processes(router_process_list) time.sleep(timeout) wait_disconnect(client_output) client_output.clear() print("Start router...") run_background(router_command, router_output, router_process_list) time.sleep(ROUTER_INIT_TIMEOUT_S) wait_reconnect(client_output) check_router_errors(router_output) print(f"Restart test {client_command} for timeout {timeout} passed") finally: terminate_processes(client_process_list + router_process_list) def test_liveliness_drop(router_command, liveliness_command, liveliness_sub_command): print(f"Liveliness drop test") router_output = [] dummy_output = [] client_output = [] process_list = [] blocked = False try: run_background(router_command, router_output, process_list) time.sleep(ROUTER_INIT_TIMEOUT_S) run_background(liveliness_sub_command, client_output, process_list) run_background(liveliness_command, dummy_output, process_list) if wait_messages(client_output, LIVELINESS_TOKEN_ALIVE_MESSAGES): print("Liveliness token alive") else: raise Exception("Failed to get liveliness token alive.") client_output.clear() print("Blocking connection...") block_connection() blocked = True time.sleep(15) if wait_messages(client_output, LIVELINESS_TOKEN_DROP_MESSAGES): print("Liveliness token dropped") else: raise Exception("Failed to get liveliness token drop.") client_output.clear() print("Unblocking connection...") unblock_connection() blocked = False if wait_messages(client_output, LIVELINESS_TOKEN_ALIVE_MESSAGES): print("Liveliness token alive") else: raise Exception("Failed to get liveliness token alive.") client_output.clear() check_router_errors(router_output) print(f"Liveliness drop test passed") finally: if blocked: unblock_connection() terminate_processes(process_list) def test_pub_sub_survive_router_restart(router_command, pub_command, sub_command, timeout): """ Start router, start subscriber and publisher, verify samples flow. Restart router, wait for clients to reconnect, verify samples still flow. """ print(f"Pub/Sub flow across router restart (timeout={timeout})") router_output = [] pub_output = [] sub_output = [] router_ps = [] pub_ps = [] sub_ps = [] try: # Router up run_background(router_command, router_output, router_ps) time.sleep(ROUTER_INIT_TIMEOUT_S) # Start subscriber first so it can declare interest run_background(sub_command, sub_output, sub_ps) wait_connect(sub_output) # session up from sub side sub_output.clear() # Start publisher run_background(pub_command, pub_output, pub_ps) wait_connect(pub_output) # session up from pub side pub_output.clear() # Confirm baseline delivery before restart print("Verifying baseline delivery...") if not wait_messages(sub_output, SUBSCRIBER_RECEIVE_MESSAGES): raise Exception("Baseline: subscriber did not receive any sample.") sub_output.clear() # Restart router print("Restarting router...") terminate_processes(router_ps) time.sleep(timeout) # Both client logs should eventually show disconnect; don't hard-fail if only one shows it try: wait_disconnect(pub_output) pub_output.clear() except Exception: pass try: wait_disconnect(sub_output) sub_output.clear() except Exception: pass run_background(router_command, router_output, router_ps) time.sleep(ROUTER_INIT_TIMEOUT_S) # Reconnect wait_reconnect(pub_output) #pub_output.clear() wait_reconnect(sub_output) sub_output.clear() # Verify delivery after restart print("Verifying delivery after router restart...") if not wait_messages(sub_output, SUBSCRIBER_RECEIVE_MESSAGES): raise Exception("After restart: subscriber did not receive any sample.") if not wait_messages(pub_output, WRITE_FILTER_OFF_MESSAGE): raise Exception("After restart: write filter state not updated to off.") check_router_errors(router_output) print("Pub/Sub flow across router restart: PASSED") finally: terminate_processes(sub_ps + pub_ps + router_ps) def test_pub_before_restart_then_new_sub(router_command, pub_command, sub_command, timeout): """ Start router and publisher; restart router; wait for publisher to reconnect; then start a new subscriber and verify it receives samples. This reproduces the writer-side filtering / missing interest issue. """ print(f"Pub before restart, new Sub after (timeout={timeout})") router_output = [] pub_output = [] sub_output = [] router_ps = [] pub_ps = [] sub_ps = [] try: # Router up run_background(router_command, router_output, router_ps) time.sleep(ROUTER_INIT_TIMEOUT_S) # Start publisher first run_background(pub_command, pub_output, pub_ps) wait_connect(pub_output) pub_output.clear() # Restart router print("Restarting router...") terminate_processes(router_ps) time.sleep(timeout) # Publisher should notice disconnect; don't fail if log line format differs try: wait_disconnect(pub_output) pub_output.clear() except Exception: pass run_background(router_command, router_output, router_ps) time.sleep(ROUTER_INIT_TIMEOUT_S) # Wait for publisher to reconnect wait_reconnect(pub_output) pub_output.clear() # Now start NEW subscriber run_background(sub_command, sub_output, sub_ps) wait_connect(sub_output) sub_output.clear() # Critical assertion: subscriber should receive samples print("Waiting for subscriber to receive samples...") if not wait_messages(sub_output, SUBSCRIBER_RECEIVE_MESSAGES): # Print some context for debugging print("=== Publisher (last lines) ===") [print(l) for l in pub_output[-50:]] print("=== Subscriber (last lines) ===") [print(l) for l in sub_output[-50:]] print("=== Router (last lines) ===") [print(l) for l in router_output[-50:]] raise Exception( "New subscriber did not receive samples after router restart while publisher " "existed before. This matches the issue where publisher stays in writer-side " "filtering and never resends interest." ) if not wait_messages(pub_output, WRITE_FILTER_OFF_MESSAGE): raise Exception("After restart: write filter state not updated to off.") check_router_errors(router_output) print("Pub before restart, new Sub after: PASSED") finally: terminate_processes(sub_ps + pub_ps + router_ps) def main(): if len(sys.argv) != 2: print("Usage: sudo python3 ./connection_restore.py /path/to/zenohd") sys.exit(1) router_command = STDBUF_CMD + [sys.argv[1]] + ROUTER_ARGS # timeout less than sesson timeout test_connection_drop(router_command, ACTIVE_CLIENT_COMMAND, 15) test_connection_drop(router_command, PASSIVE_CLIENT_COMMAND, 15) # timeout more than sesson timeout test_connection_drop(router_command, ACTIVE_CLIENT_COMMAND, 15) test_connection_drop(router_command, PASSIVE_CLIENT_COMMAND, 15) # timeout less than sesson timeout test_router_restart(router_command, ACTIVE_CLIENT_COMMAND, 8) test_router_restart(router_command, PASSIVE_CLIENT_COMMAND, 8) # timeout more than sesson timeout test_router_restart(router_command, ACTIVE_CLIENT_COMMAND, 15) test_router_restart(router_command, PASSIVE_CLIENT_COMMAND, 15) # Existing z_pub <-> z_sub communication survives a router restart test_pub_sub_survive_router_restart(router_command, ACTIVE_CLIENT_COMMAND, PASSIVE_CLIENT_COMMAND, 8) # After a router restart, a new z_sub can receive samples from a pre-restart z_pub test_pub_before_restart_then_new_sub(router_command, ACTIVE_CLIENT_COMMAND, PASSIVE_CLIENT_COMMAND, 8) test_liveliness_drop(router_command, LIVELINESS_CLIENT_COMMAND, LIVELINESS_SUB_CLIENT_COMMAND) if __name__ == "__main__": main() ================================================ FILE: tests/fragment.py ================================================ import subprocess import sys import time # Specify the directory for the binaries DIR_TESTS = "build/tests" def check_output(tx_status, tx_output, rx_status, rx_output): test_status = 0 # Expected tx output & status z_tx_expected_status = 0 z_tx_expected_output = "[tx]: Sending packet on test/zenoh-pico-fragment, len: 10000" # Expected rx output & status z_rx_expected_status = 0 z_rx_expected_output = ( "[rx]: Received packet on test/zenoh-pico-fragment, len: 10000, validity: 1, qos {priority: 4, cong_ctrl: 1}") # Check the exit status of tx if tx_status == z_tx_expected_status: print("z_tx status valid") else: print(f"z_tx status invalid, expected: {z_tx_expected_status}, received: {tx_status}") test_status = 1 # Check output of tx if z_tx_expected_output in tx_output: print("z_tx output valid") else: print("z_tx output invalid:") print(f"Expected: \"{z_tx_expected_output}\"") print(f"Received: \"{tx_output}\"") test_status = 1 # Check the exit status of z_rx if rx_status == z_rx_expected_status: print("z_rx status valid") else: print(f"z_rx status invalid, expected: {z_rx_expected_status}, received: {rx_status}") test_status = 1 # Check output of z_rx if z_rx_expected_output in rx_output: print("z_rx output valid") else: print("z_rx output invalid:") print(f"Expected: \"{z_rx_expected_output}\"") print(f"Received: \"{rx_output}\"") test_status = 1 # Return value return test_status def test_client(): # Start rx in the background print("Start rx client") z_rx_command = f"./{DIR_TESTS}/z_test_fragment_rx" z_rx_process = subprocess.Popen(z_rx_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Introduce a delay to ensure rx starts time.sleep(2) # Start tx print("Start tx client") z_tx_command = f"./{DIR_TESTS}/z_test_fragment_tx" z_tx_process = subprocess.Popen(z_tx_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Wait for tx to finish z_tx_process.wait() # Wait for rx to receive time.sleep(1) print("Stop rx") if z_rx_process.poll() is None: # Send "q" command to rx to stop it z_rx_process.stdin.write("q\n") z_rx_process.stdin.flush() # Wait for rx to finish z_rx_process.wait() # Check output return check_output(z_tx_process.returncode, z_tx_process.stdout.read(), z_rx_process.returncode, z_rx_process.stdout.read()) def test_peer(): # Start rx in the background print("Start rx peer") z_rx_command = f"./{DIR_TESTS}/z_test_fragment_rx 1" z_rx_process = subprocess.Popen(z_rx_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Introduce a delay to ensure rx starts time.sleep(2) # Start tx print("Start tx peer") z_tx_command = f"./{DIR_TESTS}/z_test_fragment_tx 1" z_tx_process = subprocess.Popen(z_tx_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Wait for tx to finish z_tx_process.wait() # Wait for rx to receive time.sleep(1) print("Stop rx") if z_rx_process.poll() is None: # Send "q" command to rx to stop it z_rx_process.stdin.write("q\n") z_rx_process.stdin.flush() # Wait for rx to finish z_rx_process.wait() # Check output return check_output(z_tx_process.returncode, z_tx_process.stdout.read(), z_rx_process.returncode, z_rx_process.stdout.read()) if __name__ == "__main__": EXIT_STATUS = 0 # Run tests if test_client() == 1: EXIT_STATUS = 1 if test_peer() == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/memory_leak.py ================================================ import os import signal import subprocess import sys import time # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" NO_LEAK_OUTPUT = "All heap blocks were freed -- no leaks are possible" VALGRIND_CMD = f"stdbuf -oL -eL valgrind --leak-check=full ./{DIR_EXAMPLES}/" def failure_mode(fail_cmd): test_status = 0 # Start binary print("Start binary") z_pub_command = VALGRIND_CMD + fail_cmd z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Wait for process to finish z_pub_process.wait() # Check output print("Check output") z_pub_output = z_pub_process.stderr.read() if NO_LEAK_OUTPUT in z_pub_output: print("Failure mode output valid") else: print("Failure mode output invalid:") print(f"Received: \"{z_pub_output}\"") test_status = 1 # Return value return test_status def pub_and_sub(pub_cmd, sub_cmd): test_status = 0 print(f"Start {sub_cmd}") # Start z_sub in the background z_sub_command = VALGRIND_CMD + sub_cmd z_sub_process = subprocess.Popen( z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Introduce a delay to ensure z_sub starts time.sleep(2) print(f"Start {pub_cmd}") # Start z_pub z_pub_command = VALGRIND_CMD + pub_cmd z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Wait for z_pub to finish z_pub_process.wait() print(f"Stop {sub_cmd}") time.sleep(2) if z_sub_process.poll() is None: # send SIGINT to group z_sub_process_gid = os.getpgid(z_sub_process.pid) os.killpg(z_sub_process_gid, signal.SIGINT) # Wait for z_sub to finish z_sub_process.wait() print("Check outputs") # Check output of z_pub z_pub_output = z_pub_process.stderr.read() if NO_LEAK_OUTPUT in z_pub_output: print(f"{pub_cmd} output valid") else: print(f"{pub_cmd} output invalid:") print(f"Received: \"{z_pub_output}\"") test_status = 1 # Check output of z_sub z_sub_output = z_sub_process.stderr.read() if NO_LEAK_OUTPUT in z_sub_output: print(f"{sub_cmd} output valid") else: print(f"{sub_cmd} output invalid:") print(f"Received: \"{z_sub_output}\"") test_status = 1 # Return value return test_status def query_and_queryable(query_cmd, queryable_cmd): test_status = 0 print(f"Start {queryable_cmd}") # Start z_queryable in the background z_queryable_command = VALGRIND_CMD + queryable_cmd z_queryable_process = subprocess.Popen( z_queryable_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Introduce a delay to ensure z_queryable starts time.sleep(2) print(f"Start {query_cmd}") # Start z_query z_query_command = VALGRIND_CMD + query_cmd z_query_process = subprocess.Popen( z_query_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Wait for z_query to finish z_query_process.wait() print(f"Stop {queryable_cmd}") time.sleep(5) if z_queryable_process.poll() is None: # send SIGINT to group z_quaryable_process_gid = os.getpgid(z_queryable_process.pid) os.killpg(z_quaryable_process_gid, signal.SIGINT) # Wait for z_queryable to finish z_queryable_process.wait() print("Check outputs") # Check output of z_query z_query_output = z_query_process.stderr.read() if NO_LEAK_OUTPUT in z_query_output: print(f"{query_cmd} output valid") else: print(f"{query_cmd} output invalid:") print(f'Received: "{z_query_output}"') test_status = 1 # Check output of z_queryable z_queryable_output = z_queryable_process.stderr.read() if NO_LEAK_OUTPUT in z_queryable_output: print(f"{queryable_cmd} output valid") else: print(f"{queryable_cmd} output invalid:") print(f'Received: "{z_queryable_output}"') test_status = 1 # Return status return test_status if __name__ == "__main__": signal.signal(signal.SIGINT, signal.SIG_IGN) EXIT_STATUS = 0 # Test failure mode print("*** Failure mode ***") if failure_mode('z_pub -m invalid') == 1: EXIT_STATUS = 1 # Test pub and sub examples print("*** Pub & sub test ***") if pub_and_sub('z_pub -n 1', 'z_sub -n 1') == 1: EXIT_STATUS = 1 print("*** Pub & sub attachment test ***") if pub_and_sub('z_pub_attachment -n 1', 'z_sub_attachment -n 1') == 1: EXIT_STATUS = 1 print("*** Pub & sub listener test ***") if pub_and_sub('z_pub -n 1 -a', 'z_sub -n 1') == 1: EXIT_STATUS = 1 # Test query and queryable examples print("*** Query & queryable test ***") if query_and_queryable('z_get', 'z_queryable -n 1') == 1: EXIT_STATUS = 1 print("*** Query & queryable attachment test ***") if query_and_queryable('z_get_attachment -v Something', 'z_queryable_attachment -n 1') == 1: EXIT_STATUS = 1 # Test querier and queryable examples print("*** Querier & queryable test ***") if query_and_queryable('z_querier -n 1', 'z_queryable -n 1') == 1: EXIT_STATUS = 1 # Test liveliness query print("*** Get liveliness test ***") if query_and_queryable('z_get_liveliness', 'z_liveliness -t 3') == 1: EXIT_STATUS = 1 # Test liveliness subscriber print("*** Liveliness subscriber test ***") if query_and_queryable('z_liveliness -t 1', 'z_sub_liveliness -h -n 1') == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/modularity.py ================================================ import argparse import os from signal import SIGINT import subprocess import sys import time import difflib # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" def print_string_diff(str_a, str_b): for i, s in enumerate(difflib.ndiff(str_a, str_b)): if s[0] == " ": continue elif s[0] == "-": print('Delete "{}" from position {}'.format(s[-1], i)) elif s[0] == "+": print('Add "{}" to position {}'.format(s[-1], i)) print() def pub_and_sub(args): print("*** Pub & sub test ***") test_status = 0 # Expected z_pub output & status if args.pub == 1: z_pub_expected_status = 0 z_pub_expected_output = """Opening session... Declaring publisher for 'demo/example/zenoh-pico-pub'... Press CTRL-C to quit... Putting Data ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')...""" else: z_pub_expected_status = 254 z_pub_expected_output = "ERROR: Zenoh pico was compiled without " "Z_FEATURE_PUBLICATION but this example requires it." # Expected z_sub output & status if args.sub == 1: if args.pub == 1: z_sub_expected_status = [0, -2] z_sub_expected_output = """Opening session... Declaring Subscriber on 'demo/example/**'... Press CTRL-C to quit... >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')""" else: z_sub_expected_status = [-2] z_sub_expected_output = """Opening session... Declaring Subscriber on 'demo/example/**'... Press CTRL-C to quit...""" else: z_sub_expected_status = [254] z_sub_expected_output = "ERROR: Zenoh pico was compiled without " "Z_FEATURE_SUBSCRIPTION but this example requires it." print("Start subscriber") # Start z_sub in the background z_sub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_sub -n 10" z_sub_process = subprocess.Popen( z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True, text=True, ) # Introduce a delay to ensure z_sub starts time.sleep(2) print("Start publisher") # Start z_pub z_pub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_pub -n 10" z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Wait for z_pub to finish z_pub_process.wait() print("Stop subscriber") if z_sub_process.poll() is None: # send SIGINT to group z_sub_process_gid = os.getpgid(z_sub_process.pid) os.killpg(z_sub_process_gid, SIGINT) # Wait for z_sub to finish z_sub_process.wait() print("Check publisher status & output") # Check the exit status of z_pub z_pub_status = z_pub_process.returncode if z_pub_status == z_pub_expected_status: print("z_pub status valid") else: print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") test_status = 1 # Check output of z_pub z_pub_output = z_pub_process.stdout.read() if z_pub_expected_output in z_pub_output: print("z_pub output valid") else: print("z_pub output invalid:") print(f'Expected: "{z_pub_expected_output}"') print(f'Received: "{z_pub_output}"') test_status = 1 print("Check subscriber status & output") # Check the exit status of z_sub z_sub_status = z_sub_process.returncode if z_sub_status in z_sub_expected_status: print("z_sub status valid") else: print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") test_status = 1 # Check output of z_sub z_sub_output = z_sub_process.stdout.read() if z_sub_expected_output in z_sub_output: print("z_sub output valid") else: print("z_sub output invalid:") print(f'Expected: "{z_sub_expected_output}"') print(f'Received: "{z_sub_output}"') test_status = 1 # Return value return test_status def query_and_queryable(args): print("*** Query & queryable test ***") test_status = 0 # Expected z_query output & status if args.query == 1: z_query_expected_status = 0 if args.queryable == 1: z_query_expected_output = """Opening session... Sending Query 'demo/example/**'... >> Received PUT ('demo/example/**': 'Queryable from Pico!') >> Received query final notification""" else: z_query_expected_output = """Opening session... Sending Query 'demo/example/**'... >> Received query final notification""" else: z_query_expected_status = 254 z_query_expected_output = ( "ERROR: Zenoh pico was compiled without " "Z_FEATURE_QUERY or Z_FEATURE_MULTI_THREAD but this example requires them." ) # Expected z_queryable output & status if args.queryable == 1: if args.query == 1: z_queryable_expected_status = [0, -2] z_queryable_expected_output = """Opening session... Creating Queryable on 'demo/example/zenoh-pico-queryable'... Press CTRL-C to quit... >> [Queryable handler] Received Query 'demo/example/**' """ else: z_queryable_expected_status = [-2] z_queryable_expected_output = """Opening session... Creating Queryable on 'demo/example/zenoh-pico-queryable'... Press CTRL-C to quit...""" else: z_queryable_expected_status = [254] z_queryable_expected_output = "ERROR: Zenoh pico was compiled without " "Z_FEATURE_QUERYABLE but this example requires it." print("Start queryable") # Start z_queryable in the background z_queryable_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_queryable -n 1" z_queryable_process = subprocess.Popen( z_queryable_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, start_new_session=True, text=True, ) # Introduce a delay to ensure z_queryable starts time.sleep(2) print("Start query") # Start z_query z_query_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_get" z_query_process = subprocess.Popen( z_query_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Wait for z_query to finish z_query_process.wait() print("Stop queryable") if z_queryable_process.poll() is None: # send SIGINT to group z_quaryable_process_gid = os.getpgid(z_queryable_process.pid) os.killpg(z_quaryable_process_gid, SIGINT) # Wait for z_queryable to finish z_queryable_process.wait() print("Check query status & output") # Check the exit status of z_query z_query_status = z_query_process.returncode if z_query_status == z_query_expected_status: print("z_query status valid") else: print(f"z_query status invalid, expected: {z_query_expected_status}," f" received: {z_query_status}") test_status = 1 # Check output of z_query z_query_output = z_query_process.stdout.read() if z_query_expected_output in z_query_output: print("z_query output valid") else: print("z_query output invalid:") print(f'Expected: "{z_query_expected_output}"') print(f'Received: "{z_query_output}"') test_status = 1 print("Check queryable status & output") # Check the exit status of z_queryable z_queryable_status = z_queryable_process.returncode if z_queryable_status in z_queryable_expected_status: print("z_queryable status valid") else: print(f"z_queryable status invalid, expected: {z_queryable_expected_status}," f" received: {z_queryable_status}") test_status = 1 # Check output of z_queryable z_queryable_output = z_queryable_process.stdout.read() if z_queryable_expected_output in z_queryable_output: print("z_queryable output valid") else: print("z_queryable output invalid:") print(f'Expected: "{z_queryable_expected_output}"') print(f'Received: "{z_queryable_output}"') print_string_diff(z_queryable_expected_output, z_queryable_output) test_status = 1 # Return status return test_status if __name__ == "__main__": parser = argparse.ArgumentParser( description="This script runs zenoh-pico examples" " and checks them according to the given configuration" ) parser.add_argument("--pub", type=int, choices=[0, 1], help="Z_FEATURE_PUBLICATION (0 or 1)") parser.add_argument("--sub", type=int, choices=[0, 1], help="Z_FEATURE_SUBSCRIPTION (0 or 1)") parser.add_argument("--queryable", type=int, choices=[0, 1], help="Z_FEATURE_QUERYABLE (0 or 1)") parser.add_argument("--query", type=int, choices=[0, 1], help="Z_FEATURE_QUERY (0 or 1)") EXIT_STATUS = 0 prog_args = parser.parse_args() print(f"Args value, pub:{prog_args.pub}, sub:{prog_args.sub}, " f"queryable:{prog_args.queryable}, query:{prog_args.query}") # Test pub and sub examples if pub_and_sub(prog_args) == 1: EXIT_STATUS = 1 # Test query and queryable examples if query_and_queryable(prog_args) == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/multicast.sh ================================================ #!/bin/sh # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # TESTBIN="$1" TESTDIR=$(dirname "$0") if [ "$OSTYPE" = "msys" ]; then TESTBIN="$TESTDIR/Debug/$TESTBIN.exe" else TESTBIN="./$TESTBIN" fi cd "$TESTDIR" || exit echo "------------------ Running test $TESTBIN -------------------" INTERFACES=$(ifconfig -l) for INTERFACE in $(echo "$INTERFACES" | xargs); do INET=$(ifconfig "$INTERFACE" | awk '$1 == "inet" {print $2}') if [ "$INET" != "127.0.0.1" ] && [ "$INET" != "" ]; then break fi done LOCATORS="udp/224.0.0.225:7447#iface=$INTERFACE" for LOCATOR in $(echo "$LOCATORS" | xargs); do sleep 1 echo "> Running $TESTBIN $LOCATOR..." "$TESTBIN" "$LOCATOR" RETCODE=$? [ "$RETCODE" -lt 0 ] && exit "$RETCODE" done INTERFACES=$(ifconfig -l) for INTERFACE in $(echo "$INTERFACES" | xargs); do INET=$(ifconfig "$INTERFACE" | awk '$1 == "inet6" {print $2}') if [ "$INET" != "::1" ] && [ "$INET" != "" ]; then break fi done LOCATORS="udp/[ff10::1234]:7447#iface=$INTERFACE" for LOCATOR in $(echo "$LOCATORS" | xargs); do sleep 1 echo "> Running $TESTBIN $LOCATOR..." "$TESTBIN" "$LOCATOR" RETCODE=$? [ "$RETCODE" -lt 0 ] && exit "$RETCODE" done echo "> Done ($RETCODE)." exit "$RETCODE" ================================================ FILE: tests/no_router.py ================================================ import subprocess import sys # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" EXPECTED_STATUS = 255 EXPECTED_OUTPUT = """Opening session... Unable to open session!""" def run_binary_test(binary_name, expected_status, expected_output): print(f"*** Testing {binary_name} ***") command = [f"./{DIR_EXAMPLES}/{binary_name}"] process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) process.wait() # Check output output = process.stdout.read() if expected_output in output: print(f"{binary_name} output valid") else: print(f'{binary_name} output invalid:\nExpected: "{expected_output}"\nReceived: "{output}"') return False # Check errors errors = process.stderr.read().strip() if errors == "": print(f"{binary_name} no error reported") else: print(f'{binary_name} errors reported:\n"{errors}"') return False # Check exit status status = process.returncode if status == expected_status: print(f"{binary_name} status valid") else: print(f"{binary_name} status invalid, expected: {expected_status}, received: {status}") return False return True def test_all(): binaries = ["z_sub", "z_pub", "z_queryable", "z_get", "z_liveliness", "z_get_liveliness", "z_sub_liveliness"] all_tests_passed = True for binary in binaries: if not run_binary_test(binary, EXPECTED_STATUS, EXPECTED_OUTPUT): all_tests_passed = False return all_tests_passed if __name__ == "__main__": EXIT_STATUS = 0 # Run all tests if not test_all(): EXIT_STATUS = 1 # Exit with final status sys.exit(EXIT_STATUS) ================================================ FILE: tests/package_mylinux.sh.in ================================================ #!/usr/bin/env bash set -euo pipefail SOURCE_DIR="@PROJECT_SOURCE_DIR@" WORK_DIR="$(mktemp -d "${PWD}/zenoh-pico-package-mylinux-XXXXXX")" trap 'rm -rf "$WORK_DIR"' EXIT EXAMPLE_DIR="$SOURCE_DIR/examples/packages/zenohpico-mylinux" PREFIX_DIR="$WORK_DIR/prefix" PACKAGE_BUILD_DIR="$WORK_DIR/package-build" SHARED_BUILD_DIR="$WORK_DIR/shared-build" STATIC_BUILD_DIR="$WORK_DIR/static-build" INSTALL_DIR="$WORK_DIR/install" CONSUMER_BUILD_DIR="$WORK_DIR/consumer-build" cmake -S "$EXAMPLE_DIR" -B "$PACKAGE_BUILD_DIR" \ -DZENOH_PICO_SOURCE_DIR="$SOURCE_DIR" \ -DZENOH_PICO_CONFIG_INCLUDE_DIR="@PROJECT_BINARY_DIR@/include" cmake --build "$PACKAGE_BUILD_DIR" -j cmake --install "$PACKAGE_BUILD_DIR" --prefix "$PREFIX_DIR" cmake -S "$SOURCE_DIR" -B "$SHARED_BUILD_DIR" \ -DZ_FEATURE_MULTI_THREAD=@Z_FEATURE_MULTI_THREAD@ \ -DZ_FEATURE_LINK_SERIAL=@Z_FEATURE_LINK_SERIAL@ \ -DBUILD_EXAMPLES=OFF \ -DZP_EXTERNAL_PACKAGES=zenohpico-mylinux \ -DCMAKE_PREFIX_PATH="$PREFIX_DIR" \ -DZP_PLATFORM=mylinux cmake --build "$SHARED_BUILD_DIR" -j --target \ z_endpoint_test \ z_tls_test \ z_executor_test \ z_background_executor_test \ z_local_loopback_test ctest --test-dir "$SHARED_BUILD_DIR" --output-on-failure \ -R '^z_endpoint_test$|^z_tls_test$|^z_executor_test$|^z_background_executor_test$|^z_local_loopback_test$' \ --timeout 420 cmake -S "$SOURCE_DIR" -B "$STATIC_BUILD_DIR" \ -DZ_FEATURE_MULTI_THREAD=@Z_FEATURE_MULTI_THREAD@ \ -DZ_FEATURE_LINK_SERIAL=@Z_FEATURE_LINK_SERIAL@ \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF \ -DZP_EXTERNAL_PACKAGES=zenohpico-mylinux \ -DCMAKE_PREFIX_PATH="$PREFIX_DIR" \ -DZP_PLATFORM=mylinux cmake --build "$STATIC_BUILD_DIR" -j --target zenohpico_static cmake --install "$STATIC_BUILD_DIR" --prefix "$INSTALL_DIR" cmake -S "$EXAMPLE_DIR/consumer" -B "$CONSUMER_BUILD_DIR" \ -DCMAKE_PREFIX_PATH="$INSTALL_DIR;$PREFIX_DIR" cmake --build "$CONSUMER_BUILD_DIR" -j --target consumer ================================================ FILE: tests/package_myrtos.sh.in ================================================ #!/usr/bin/env bash set -euo pipefail SOURCE_DIR="@PROJECT_SOURCE_DIR@" WORK_DIR="$(mktemp -d "${PWD}/zenoh-pico-package-myrtos-XXXXXX")" trap 'rm -rf "$WORK_DIR"' EXIT PREFIX_DIR="$WORK_DIR/prefix" PACKAGE_DIR="$PREFIX_DIR/lib/cmake/zenohpico-myrtos" PLATFORM_DIR="$PACKAGE_DIR/platforms" LIB_DIR="$PREFIX_DIR/lib" BUILD_DIR="$WORK_DIR/build" mkdir -p "$PLATFORM_DIR" "$LIB_DIR" cat >"$PLATFORM_DIR/myrtos.cmake" <<'EOF' set(ZP_PLATFORM_COMPILE_DEFINITIONS ZENOH_FREERTOS_LWIP) set(ZP_PLATFORM_SOURCE_FILES "@PROJECT_SOURCE_DIR@/src/system/socket/lwip.c" "@PROJECT_SOURCE_DIR@/src/link/transport/tcp/tcp_lwip.c" "@PROJECT_SOURCE_DIR@/src/link/transport/udp/udp_lwip.c") if(ZP_UDP_MULTICAST_ENABLED) list(APPEND ZP_PLATFORM_SOURCE_FILES "@PROJECT_SOURCE_DIR@/src/link/transport/udp/udp_multicast_lwip.c" "@PROJECT_SOURCE_DIR@/src/link/transport/udp/udp_multicast_lwip_common.c") endif() list(APPEND ZP_PLATFORM_LINK_LIBRARIES myrtos::system myrtos::serial_uart) EOF : >"$LIB_DIR/libmyrtos_system.a" : >"$LIB_DIR/libmyrtos_serial_uart.a" cat >"$PACKAGE_DIR/zenohpico-myrtosConfig.cmake" <<'EOF' if(COMMAND zp_add_platform_dir) zp_add_platform_dir("${CMAKE_CURRENT_LIST_DIR}/platforms") endif() add_library(myrtos::system STATIC IMPORTED GLOBAL) set_target_properties(myrtos::system PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../../libmyrtos_system.a") add_library(myrtos::serial_uart STATIC IMPORTED GLOBAL) set_target_properties(myrtos::serial_uart PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../../libmyrtos_serial_uart.a") EOF cmake -S "$SOURCE_DIR" -B "$BUILD_DIR" \ -DZ_FEATURE_MULTI_THREAD=@Z_FEATURE_MULTI_THREAD@ \ -DZ_FEATURE_LINK_SERIAL=@Z_FEATURE_LINK_SERIAL@ \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTING=OFF \ -DZ_FEATURE_LINK_UDP_MULTICAST=0 \ -DZ_FEATURE_MULTICAST_TRANSPORT=0 \ -DZP_EXTERNAL_PACKAGES=zenohpico-myrtos \ -DCMAKE_PREFIX_PATH="$PREFIX_DIR" \ -DZP_PLATFORM=myrtos ================================================ FILE: tests/raweth.py ================================================ import argparse import os from signal import SIGINT import subprocess import sys import time # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" def pub_and_sub(args): print("*** Pub & sub test ***") test_status = 0 # Expected z_pub output & status if args.reth == 1: z_pub_expected_status = 0 z_pub_expected_output = '''Opening session... Waiting for joins... Declaring publisher for 'demo/example/zenoh-pico-pub'... Press CTRL-C to quit... Putting Data ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')...''' else : z_pub_expected_status = 255 z_pub_expected_output = '''Opening session... Unable to open session!''' # Expected z_sub output & status if args.reth == 1: z_sub_expected_status = 0 z_sub_expected_output = '''Opening session... Declaring Subscriber on 'demo/example/**'... Press CTRL-C to quit... >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')''' else : z_sub_expected_status = 255 z_sub_expected_output = '''Opening session... Unable to open session!''' print("Start subscriber") # Start z_sub in the background z_sub_command = f"sudo stdbuf -oL -eL ./{DIR_EXAMPLES}/z_sub -n 10 -m \"peer\" -l \ \"reth/30:03:8c:c8:00:a2#iface=lo;whitelist=30:03:8c:c8:00:a1,\"s" z_sub_process = subprocess.Popen(z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Introduce a delay to ensure z_sub starts time.sleep(2) print("Start publisher") # Start z_pub z_pub_command = f"sudo stdbuf -oL -eL ./{DIR_EXAMPLES}/z_pub -n 10 -m \"peer\" -l \ \"reth/30:03:8c:c8:00:a1#iface=lo;whitelist=30:03:8c:c8:00:a2,\"s" z_pub_process = subprocess.Popen(z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Wait for z_pub to finish z_pub_process.wait() print("Stop subscriber") time.sleep(2) if z_sub_process.poll() is None: # send SIGINT to group z_sub_process_gid = os.getpgid(z_sub_process.pid) os.killpg(z_sub_process_gid, SIGINT) # Wait for z_sub to finish z_sub_process.wait() print("Check publisher status & output") # Check the exit status of z_pub z_pub_status = z_pub_process.returncode if z_pub_status == z_pub_expected_status: print("z_pub status valid") else: print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") test_status = 1 # Check output of z_pub z_pub_output = z_pub_process.stdout.read() if z_pub_expected_output in z_pub_output: print("z_pub output valid") else: print("z_pub output invalid:") print(f"Expected: \"{z_pub_expected_output}\"") print(f"Received: \"{z_pub_output}\"") test_status = 1 print("Check subscriber status & output") # Check the exit status of z_sub z_sub_status = z_sub_process.returncode if z_sub_status == z_sub_expected_status: print("z_sub status valid") else: print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") test_status = 1 # Check output of z_sub z_sub_output = z_sub_process.stdout.read() if z_sub_expected_output in z_sub_output: print("z_sub output valid") else: print("z_sub output invalid:") print(f"Expected: \"{z_sub_expected_output}\"") print(f"Received: \"{z_sub_output}\"") test_status = 1 # Return value return test_status if __name__ == "__main__": parser = argparse.ArgumentParser(description="This script runs zenoh-pico examples" " and checks them according to the given configuration") parser.add_argument("--reth", type=int, choices=[0, 1], help="Z_FEATURE_RAWETH_TRANSPORT (0 or 1)") EXIT_STATUS = 0 prog_args = parser.parse_args() print(f"Args value, reth:{prog_args.reth}") # Test pub and sub examples if pub_and_sub(prog_args) == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/routed.sh ================================================ #!/bin/sh # # Copyright (c) 2022 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # TESTBIN="$1" TESTDIR=$(cd "$(dirname "$0")" && pwd) if [ "$OSTYPE" = "msys" ]; then TESTBIN="$TESTDIR/Debug/$TESTBIN.exe" else TESTBIN="./$TESTBIN" fi cd "$TESTDIR" || exit echo "------------------ Running test $TESTBIN -------------------" sleep 5 # Clean up if zenohd exists but is not a regular file (handles corrupted state from interrupted runs) if [ -e zenohd ] && [ ! -f zenohd ]; then echo "Warning: zenohd exists but is not a regular file, removing..." rm -rf zenohd fi if [ ! -f zenohd ]; then if [ ! -d zenoh-git ]; then git clone https://github.com/eclipse-zenoh/zenoh.git zenoh-git fi cd zenoh-git || exit if [ -n "$ZENOH_BRANCH" ]; then git switch "$ZENOH_BRANCH" fi rustup show cargo build --lib --bin zenohd cp -f ./target/debug/zenohd "$TESTDIR"/ cd "$TESTDIR" || exit fi chmod +x zenohd # Verify zenohd is ready to use if [ ! -f zenohd ]; then echo "ERROR: zenohd binary not found after build attempt" exit 1 fi if [ ! -x zenohd ]; then echo "ERROR: zenohd binary is not executable" exit 1 fi LOCATORS="tcp/127.0.0.1:7447 tcp/[::1]:7447 udp/127.0.0.1:7447 udp/[::1]:7447" ENABLE_TLS=0 if [ "$2" = "--enable-tls" ]; then ENABLE_TLS=1 LOCATORS="$LOCATORS tls/127.0.0.1:7447#root_ca_certificate=$TESTDIR/ca.pem tls/[::1]:7447#root_ca_certificate=$TESTDIR/ca.pem" rm -f ca.pem ca-key.pem ca.srl server.pem server-key.pem server.csr cat > server.cnf < Running zenohd ... $LOCATOR" if [ "$ENABLE_TLS" = "1" ]; then cat > zenohd_tls.json < zenohd."$1".log 2>&1 & else RUST_LOG=debug ./zenohd --plugin-search-dir "$TESTDIR/zenoh-git/target/debug" -l "$LOCATOR" > zenohd."$1".log 2>&1 & fi ZPID=$! sleep 5 # Verify zenohd is actually running if ! kill -0 "$ZPID" 2>/dev/null; then echo "> ERROR: zenohd failed to start (PID: $ZPID)" echo "> Logs of zenohd ..." cat zenohd."$1".log exit 1 fi echo "> Running $TESTBIN ..." "$TESTBIN" "$LOCATOR" > client."$1".log 2>&1 RETCODE=$? echo "> Stopping zenohd ..." if kill -0 "$ZPID" 2>/dev/null; then kill -9 "$ZPID" wait "$ZPID" 2>/dev/null else echo "> WARNING: zenohd (PID: $ZPID) was not running" fi sleep 1 if [ "$RETCODE" -lt 0 ]; then echo "> Logs of client ..." cat client."$1".log echo "> Logs of zenohd ..." cat zenohd."$1".log exit "$RETCODE" fi done echo "> Done ($RETCODE)." exit "$RETCODE" ================================================ FILE: tests/routed_peer_client.py ================================================ """Test suite for pub (peer) > router > sub (client) scenarios.""" import os from signal import SIGINT import subprocess import sys import time import re # pylint: disable=consider-using-with # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" def pub_and_sub(pub_args, sub_args, pub_first=True): """ Run a publisher and subscriber test. Parameters: pub_args (str): Arguments passed to the publisher binary. sub_args (str): Arguments passed to the subscriber binary. pub_first (bool): If True, start publisher first; otherwise start subscriber first. Returns: int: 0 if the test passes, 1 if it fails. """ test_status = 0 # Expected z_pub status z_pub_expected_status = 0 # Expected z_sub output & status z_sub_expected_status = 0 z_sub_expected_pattern = re.compile( r">> \[Subscriber\] Received \('demo/example/zenoh-pico-pub': '\[.*?\] Pub from Pico!'\)" ) z_pub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_pub {pub_args} -n 10" z_sub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_sub {sub_args} -n 1" print("PUB CMD:", z_pub_command) print("SUB CMD:", z_sub_command) if pub_first: print("Start publisher") # Start z_pub first z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Give publisher time to start time.sleep(2) print("Start subscriber") # Then start z_sub z_sub_process = subprocess.Popen( z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, start_new_session=True, ) else: print("Start subscriber") # Start z_sub first z_sub_process = subprocess.Popen( z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, start_new_session=True, ) # Give subscriber time to start time.sleep(2) print("Start publisher") # Then start z_pub z_pub_process = subprocess.Popen( z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) # Wait for z_pub to finish and capture its output pub_stdout, pub_stderr = z_pub_process.communicate() print("Stop subscriber") if z_sub_process.poll() is None: # send SIGINT to group (safe because of start_new_session=True) z_sub_process_gid = os.getpgid(z_sub_process.pid) os.killpg(z_sub_process_gid, SIGINT) # Ensure subscriber has exited and capture its output sub_stdout, sub_stderr = z_sub_process.communicate() print("Check publisher status") # Check the exit status of z_pub z_pub_status = z_pub_process.returncode if z_pub_status == z_pub_expected_status: print("z_pub status valid") else: print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") test_status = 1 print("Check subscriber status & output") # Check the exit status of z_sub z_sub_status = z_sub_process.returncode if z_sub_status == z_sub_expected_status: print("z_sub status valid") else: print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") test_status = 1 # Check the output of z_sub if z_sub_expected_pattern.search(sub_stdout): print("z_sub output valid") else: print("z_sub output invalid:") test_status = 1 # If anything failed, dump stdout/stderr for both processes if test_status == 1: print("==== z_pub STDOUT ====") print(pub_stdout or "") print("==== z_pub STDERR ====") print(pub_stderr or "") print("==== z_sub STDOUT ====") print(sub_stdout or "") print("==== z_sub STDERR ====") print(sub_stderr or "") # Return value return test_status def test_tcp_unicast(locator, pub_first=True): """Run TCP unicast pub/sub test.""" print(f"*** TCP Unicast Test (pub_first={pub_first}) ***") pub_args = f"-m peer -e {locator}" sub_args = f"-m client -e {locator}" return pub_and_sub(pub_args, sub_args, pub_first) def test_udp_multicast(tcp_locator, udp_locator, pub_first=True): """Run UDP multicast pub/sub test.""" print(f"*** UDP Multicast Test (pub_first={pub_first}) ***") pub_args = f"-m peer -l {udp_locator}" sub_args = f"-m client -e {tcp_locator}" return pub_and_sub(pub_args, sub_args, pub_first) if __name__ == "__main__": EXIT_STATUS = 0 TCP_UNICAST_LOCATOR = None # pylint: disable=invalid-name UDP_MULTICAST_LOCATOR = None # pylint: disable=invalid-name args = sys.argv[1:] for arg in args: if arg.startswith("tcp/"): TCP_UNICAST_LOCATOR = arg elif arg.startswith("udp/"): UDP_MULTICAST_LOCATOR = arg print("TCP unicast locator:", TCP_UNICAST_LOCATOR) print("UDP multicast locator:", UDP_MULTICAST_LOCATOR) if TCP_UNICAST_LOCATOR is not None: if test_tcp_unicast(TCP_UNICAST_LOCATOR, True) == 1: EXIT_STATUS = 1 if test_tcp_unicast(TCP_UNICAST_LOCATOR, False) == 1: EXIT_STATUS = 1 else: print("No TCP unicast locator provided, skipping test_tcp_unicast.") if TCP_UNICAST_LOCATOR is not None and UDP_MULTICAST_LOCATOR is not None: if test_udp_multicast(TCP_UNICAST_LOCATOR, UDP_MULTICAST_LOCATOR, True) == 1: EXIT_STATUS = 1 if test_udp_multicast(TCP_UNICAST_LOCATOR, UDP_MULTICAST_LOCATOR, False) == 1: EXIT_STATUS = 1 else: print("No TCP unicast or UDP multicast locators provided, skipping test_udp_multicast.") sys.exit(EXIT_STATUS) ================================================ FILE: tests/single_thread.py ================================================ import subprocess import signal import sys import time # Specify the directory for the binaries DIR_EXAMPLES = "build/examples" def pub_and_sub(): print("*** Pub & sub test ***") test_status = 0 # Expected z_pub output & status z_pub_expected_status = 0 z_pub_expected_output = '''Opening session... Declaring publisher for 'demo/example/zenoh-pico-pub'... Press CTRL-C to quit... Putting Data ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!')... Putting Data ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')...''' # Expected z_sub output z_sub_expected_status = 0 z_sub_expected_output = '''Opening session... Declaring Subscriber on 'demo/example/**'... Press CTRL-C to quit... >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 0] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 1] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 2] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 3] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 4] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 5] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 6] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 7] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 8] Pub from Pico!') >> [Subscriber] Received ('demo/example/zenoh-pico-pub': '[ 9] Pub from Pico!')''' print("Start subscriber") # Start z_sub in the background z_sub_command = f"./{DIR_EXAMPLES}/z_sub_st -n 10" z_sub_process = subprocess.Popen(z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Introduce a delay to ensure z_sub starts time.sleep(2) print("Start publisher") # Start z_pub z_pub_command = f"./{DIR_EXAMPLES}/z_pub_st -n 10" z_pub_process = subprocess.Popen(z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) # Wait for z_pub to finish z_pub_process.wait() z_sub_process.wait() print("Check publisher status & output") # Check the exit status of z_pub z_pub_status = z_pub_process.returncode if z_pub_status == z_pub_expected_status: print("z_pub status valid") else: print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") test_status = 1 # Check output of z_pub z_pub_output = z_pub_process.stdout.read() if z_pub_expected_output in z_pub_output: print("z_pub output valid") else: print("z_pub output invalid:") print(f"Expected: \"{z_pub_expected_output}\"") print(f"Received: \"{z_pub_output}\"") test_status = 1 print("Check subscriber status & output") # Check the exit status of z_sub z_sub_status = z_sub_process.returncode if z_sub_status == z_sub_expected_status: print("z_sub status valid") else: print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") test_status = 1 # Check output of z_sub z_sub_output = z_sub_process.stdout.read() if z_sub_expected_output in z_sub_output: print("z_sub output valid") else: print("z_sub output invalid:") print(f"Expected: \"{z_sub_expected_output}\"") print(f"Received: \"{z_sub_output}\"") test_status = 1 # Return value return test_status if __name__ == "__main__": EXIT_STATUS = 0 # Test pub and sub examples if pub_and_sub() == 1: EXIT_STATUS = 1 # Exit sys.exit(EXIT_STATUS) ================================================ FILE: tests/tls_verify.sh ================================================ #!/bin/sh # # Copyright (c) 2025 ZettaScale Technology # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 # which is available at https://www.apache.org/licenses/LICENSE-2.0. # # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: # ZettaScale Zenoh Team, # TESTBIN="$1" TESTDIR=$(dirname "$0") if [ "$OSTYPE" = "msys" ]; then TESTBIN="$TESTDIR/Debug/$TESTBIN.exe" else TESTBIN="./$TESTBIN" fi cd "$TESTDIR" || exit 1 if [ ! -f zenohd ]; then git clone https://github.com/eclipse-zenoh/zenoh.git zenoh-git cd zenoh-git || exit 1 if [ -n "$ZENOH_BRANCH" ]; then git switch "$ZENOH_BRANCH" fi cargo build --lib --bin zenohd cp ./target/debug/zenohd "$TESTDIR"/ cd "$TESTDIR" || exit 1 fi chmod +x zenohd rm -f ca.pem ca-key.pem ca.srl server.pem server-key.pem server.csr cat > server.cnf < client.cnf < zenohd_tls.json < Running ./zenohd -c $config_file -l ${LOC_BASE} ..." RUST_LOG=warn ./zenohd -c "$config_file" --plugin-search-dir "$TESTDIR/zenoh-git/target/debug" \ -l "$LOC_BASE" > zenohd.z_tls_verify.log 2>&1 & ZPID=$! sleep 5 echo "> Running $TESTBIN \"$locator\" ..." "$TESTBIN" "$locator" --msgs=1 > client.z_tls_verify.log 2>&1 RETCODE=$? echo "> Stopping zenohd ..." kill -9 "$ZPID" 2>/dev/null || true wait "$ZPID" 2>/dev/null || true sleep 1 if [ "$expect_success" = "1" ]; then if [ "$RETCODE" -ne 0 ]; then echo "Expected success but client exited with $RETCODE" >&2 cat client.z_tls_verify.log >&2 exit 1 fi else if [ "$RETCODE" -eq 0 ]; then echo "Expected failure but client succeeded" >&2 cat client.z_tls_verify.log >&2 exit 1 fi fi } run_test "$LOC_BASE#root_ca_certificate=$TESTDIR/ca.pem" 0 run_test "$LOC_BASE#root_ca_certificate=$TESTDIR/ca.pem;verify_name_on_connect=0" 1 cat > zenohd_tls_mtls.json < // #ifndef ZP_ASSERT_HELPERS_H #define ZP_ASSERT_HELPERS_H #include #include #include #include #include "zenoh-pico/utils/result.h" #ifdef NDEBUG #include #endif // Fallbacks for older libcs #ifndef PRIu32 #define PRIu32 "u" #endif #ifndef PRIu64 #define PRIu64 "llu" #endif #ifndef PRIi32 #define PRIi32 "d" #endif // Trap that fails in both debug and release builds #ifndef NDEBUG #define _ZP_TEST_TRAP() (assert(0 && "test assertion failed")) #else #define _ZP_TEST_TRAP() (abort()) #endif // ---------- Assertions ---------- #define ASSERT_TRUE(expr) \ do { \ bool _zp_v_ = (expr); \ if (!_zp_v_) { \ fprintf(stderr, "[FAIL] %s:%d: expected %s to be true\n", __FILE__, __LINE__, #expr); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_FALSE(expr) \ do { \ bool _zp_v_ = (expr); \ if (_zp_v_) { \ fprintf(stderr, "[FAIL] %s:%d: expected %s to be false\n", __FILE__, __LINE__, #expr); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_EQ_U32(a, b) \ do { \ uint32_t _zp_a_ = (uint32_t)(a); \ uint32_t _zp_b_ = (uint32_t)(b); \ if (_zp_a_ != _zp_b_) { \ fprintf(stderr, "[FAIL] %s:%d: %s (%" PRIu32 ") != %s (%" PRIu32 ")\n", __FILE__, __LINE__, #a, _zp_a_, \ #b, _zp_b_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_EQ_U64(a, b) \ do { \ uint64_t _zp_a_ = (uint64_t)(a); \ uint64_t _zp_b_ = (uint64_t)(b); \ if (_zp_a_ != _zp_b_) { \ fprintf(stderr, "[FAIL] %s:%d: %s (%" PRIu64 ") != %s (%" PRIu64 ")\n", __FILE__, __LINE__, #a, _zp_a_, \ #b, _zp_b_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_EQ_I32(a, b) \ do { \ int32_t _zp_a_ = (int32_t)(a); \ int32_t _zp_b_ = (int32_t)(b); \ if (_zp_a_ != _zp_b_) { \ fprintf(stderr, "[FAIL] %s:%d: %s (%" PRIi32 ") != %s (%" PRIi32 ")\n", __FILE__, __LINE__, #a, _zp_a_, \ #b, _zp_b_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_EQ_PTR(a, b) \ do { \ const void *_zp_a_ = (const void *)(a); \ const void *_zp_b_ = (const void *)(b); \ if (_zp_a_ != _zp_b_) { \ fprintf(stderr, "[FAIL] %s:%d: %s (%p) != %s (%p)\n", __FILE__, __LINE__, #a, _zp_a_, #b, _zp_b_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #define ASSERT_OK(expr) \ do { \ z_result_t _zp_res_ = (expr); \ if (_zp_res_ != _Z_RES_OK) { \ fprintf(stderr, "[FAIL] %s:%d: %s returned %d\n", __FILE__, __LINE__, #expr, (int)_zp_res_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) // Expect any error (i.e., not _Z_RES_OK) #define ASSERT_NOT_OK(expr) \ do { \ z_result_t _zp_res_ = (expr); \ if (_zp_res_ == _Z_RES_OK) { \ fprintf(stderr, "[FAIL] %s:%d: %s returned OK (expected error)\n", __FILE__, __LINE__, #expr); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) // Expect a specific error/result code #define ASSERT_ERR(expr, expected) \ do { \ z_result_t _zp_res_ = (expr); \ z_result_t _zp_exp_ = (expected); \ if (_zp_res_ != _zp_exp_) { \ fprintf(stderr, "[FAIL] %s:%d: %s returned %d (expected %d)\n", __FILE__, __LINE__, #expr, (int)_zp_res_, \ (int)_zp_exp_); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) // Expect a non null pointer #define ASSERT_NOT_NULL(ptr) \ do { \ const void *_zp_p_ = (const void *)(ptr); \ if (_zp_p_ == NULL) { \ fprintf(stderr, "[FAIL] %s:%d: %s is NULL\n", __FILE__, __LINE__, #ptr); \ fflush(stderr); \ _ZP_TEST_TRAP(); \ } \ } while (0) #endif // ZP_ASSERT_HELPERS_H ================================================ FILE: tests/utils/tcp_proxy.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // // Copyright (c) 2025 ZettaScale Technology // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // #include "tcp_proxy.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef TCP_MAX_CONNS #define TCP_MAX_CONNS 8 #endif #ifndef TCP_BUFSZ #define TCP_BUFSZ 65536 #endif typedef int sock_t; typedef struct { sock_t cli; sock_t up; } tcp_pair_t; struct tcp_proxy { char lhost[64]; char thost[64]; uint16_t tport; volatile bool run; volatile bool enabled; pthread_t thr; sock_t lsock; // listening socket tcp_pair_t conns[TCP_MAX_CONNS]; int nconns; pthread_mutex_t mtx; // guards enabled + conns[] }; // ---- utilities ---- static void msleep_(int ms) { struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L}; nanosleep(&ts, NULL); } static int set_nonblock(sock_t s) { int fl = fcntl(s, F_GETFL, 0); return fcntl(s, F_SETFL, fl | O_NONBLOCK); } static sock_t tcp_listen4_ephemeral(const char* host) { struct sockaddr_in sa = {0}; sa.sin_family = AF_INET; sa.sin_port = htons(0); // ephemeral if (host == NULL) { host = "127.0.0.1"; } if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) { return -1; } sock_t s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { return -1; } int one = 1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); #ifdef SO_REUSEPORT setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); #endif if (bind(s, (struct sockaddr*)&sa, sizeof sa) != 0) { close(s); return -1; } if (listen(s, 64) != 0) { close(s); return -1; } (void)set_nonblock(s); return s; } static int get_sock_port(sock_t s) { struct sockaddr_in sa; socklen_t sl = sizeof sa; if (getsockname(s, (struct sockaddr*)&sa, &sl) != 0) { return -1; } return (int)ntohs(sa.sin_port); } static int tcp_connect4(const char* host, uint16_t port) { char pbuf[16]; snprintf(pbuf, sizeof pbuf, "%u", (unsigned)port); struct addrinfo hints = {0}; struct addrinfo* res = NULL; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo((host != NULL) ? host : "127.0.0.1", pbuf, &hints, &res) != 0) { return -1; } sock_t s = -1; for (struct addrinfo* ai = res; ai != NULL; ai = ai->ai_next) { s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { continue; } if (connect(s, ai->ai_addr, ai->ai_addrlen) == 0) { break; } close(s); s = -1; } freeaddrinfo(res); if (s >= 0) { (void)set_nonblock(s); } return s; } static void pair_close(tcp_pair_t* pr) { if (pr->cli >= 0) { close(pr->cli); pr->cli = -1; } if (pr->up >= 0) { close(pr->up); pr->up = -1; } } // ---- thread ---- // When disabled, we still recv() (to ACK at TCP level) but DROP data instead of forwarding. static void* proxy_thread(void* arg) { tcp_proxy_t* p = (tcp_proxy_t*)arg; while (p->run) { fd_set rfds; FD_ZERO(&rfds); FD_SET(p->lsock, &rfds); int maxfd = p->lsock; pthread_mutex_lock(&p->mtx); for (int i = 0; i < p->nconns; i++) { if (p->conns[i].cli >= 0) { FD_SET(p->conns[i].cli, &rfds); if (p->conns[i].cli > maxfd) { maxfd = p->conns[i].cli; } } if (p->conns[i].up >= 0) { FD_SET(p->conns[i].up, &rfds); if (p->conns[i].up > maxfd) { maxfd = p->conns[i].up; } } } bool forward_enabled = p->enabled; pthread_mutex_unlock(&p->mtx); struct timeval tv = (struct timeval){.tv_sec = 0, .tv_usec = 200 * 1000}; // 200 ms int ready = select(maxfd + 1, &rfds, NULL, NULL, &tv); if (ready <= 0) { continue; } // Accept new clients if (FD_ISSET(p->lsock, &rfds)) { struct sockaddr_in cli; socklen_t clen = sizeof cli; int cs = accept(p->lsock, (struct sockaddr*)&cli, &clen); if (cs >= 0) { (void)set_nonblock(cs); int ups = tcp_connect4(p->thost, p->tport); if (ups < 0) { close(cs); } else { pthread_mutex_lock(&p->mtx); if (p->nconns < TCP_MAX_CONNS) { p->conns[p->nconns].cli = cs; p->conns[p->nconns].up = ups; p->nconns++; } else { close(cs); close(ups); } pthread_mutex_unlock(&p->mtx); } } } // Pump both directions pthread_mutex_lock(&p->mtx); for (int i = 0; i < p->nconns; i++) { tcp_pair_t* pr = &p->conns[i]; char buf[TCP_BUFSZ]; // client -> upstream if (pr->cli >= 0 && FD_ISSET(pr->cli, &rfds)) { ssize_t nread = recv(pr->cli, buf, sizeof buf, 0); if (nread <= 0) { pair_close(pr); } else { if (forward_enabled) { size_t off = 0; size_t total = (size_t)nread; while (off < total) { ssize_t nw = send(pr->up, buf + off, total - off, 0); if (nw <= 0) { pair_close(pr); break; } off += (size_t)nw; } } // else DROP: consumed but not forwarded (client sees ACKs) } } // upstream -> client if (pr->up >= 0 && FD_ISSET(pr->up, &rfds)) { ssize_t nread = recv(pr->up, buf, sizeof buf, 0); if (nread <= 0) { pair_close(pr); } else { if (forward_enabled) { size_t off = 0; size_t total = (size_t)nread; while (off < total) { ssize_t nw = send(pr->cli, buf + off, total - off, 0); if (nw <= 0) { pair_close(pr); break; } off += (size_t)nw; } } // else DROP: consumed but not forwarded } } } // Compact live pairs int w = 0; for (int r = 0; r < p->nconns; r++) { if (p->conns[r].cli >= 0) { p->conns[w++] = p->conns[r]; } } p->nconns = w; pthread_mutex_unlock(&p->mtx); } return NULL; } // ---- API ---- tcp_proxy_t* tcp_proxy_create(const char* listen_host, const char* target_host, uint16_t target_port) { tcp_proxy_t* p = calloc(1, sizeof *p); if (p == NULL) { return NULL; } snprintf(p->lhost, sizeof p->lhost, "%s", (listen_host != NULL) ? listen_host : "127.0.0.1"); snprintf(p->thost, sizeof p->thost, "%s", (target_host != NULL) ? target_host : "127.0.0.1"); p->tport = target_port; p->run = false; p->enabled = true; p->lsock = -1; p->nconns = 0; for (int i = 0; i < TCP_MAX_CONNS; i++) { p->conns[i].cli = -1; p->conns[i].up = -1; } pthread_mutex_init(&p->mtx, NULL); return p; } int tcp_proxy_start(tcp_proxy_t* p, int ms_timeout) { if (p == NULL) { return -1; // invalid arg } if (p->run) { int cur = (p->lsock >= 0) ? get_sock_port(p->lsock) : -3; return (cur >= 0) ? cur : -3; } // Create listening socket now so we know the ephemeral port. p->lsock = tcp_listen4_ephemeral(p->lhost); if (p->lsock < 0) { return -2; // listen failed } int port = get_sock_port(p->lsock); if (port < 0) { close(p->lsock); p->lsock = -1; return -3; // getsockname failed } p->run = true; if (pthread_create(&p->thr, NULL, proxy_thread, p) != 0) { p->run = false; close(p->lsock); p->lsock = -1; return -4; // thread failed } // Optional grace period for the thread to enter its loop. if (ms_timeout > 0) { int waited = 0; const int step = 10; while (waited < ms_timeout) { msleep_(step); waited += step; } } return port; // success: ephemeral port } int tcp_proxy_stop(tcp_proxy_t* p) { if (p == NULL) { return -1; } if (!p->run) { return 0; } p->run = false; pthread_join(p->thr, NULL); // Close listening socket and all pairs. if (p->lsock >= 0) { close(p->lsock); p->lsock = -1; } pthread_mutex_lock(&p->mtx); for (int i = 0; i < p->nconns; i++) { pair_close(&p->conns[i]); } p->nconns = 0; pthread_mutex_unlock(&p->mtx); return 0; } void tcp_proxy_destroy(tcp_proxy_t* p) { if (p == NULL) { return; } if (p->run) { (void)tcp_proxy_stop(p); } if (p->lsock >= 0) { close(p->lsock); p->lsock = -1; } pthread_mutex_destroy(&p->mtx); free(p); } void tcp_proxy_set_enabled(tcp_proxy_t* p, bool enabled) { if (p == NULL) { return; } pthread_mutex_lock(&p->mtx); p->enabled = enabled; pthread_mutex_unlock(&p->mtx); } bool tcp_proxy_get_enabled(const tcp_proxy_t* p) { if (p == NULL) { return false; } return p->enabled; } void tcp_proxy_close_all(tcp_proxy_t* p) { if (p == NULL) { return; } pthread_mutex_lock(&p->mtx); for (int i = 0; i < p->nconns; i++) { pair_close(&p->conns[i]); } p->nconns = 0; pthread_mutex_unlock(&p->mtx); } void tcp_proxy_drop_for_ms(tcp_proxy_t* p, int ms) { if (p == NULL) { return; } tcp_proxy_set_enabled(p, false); msleep_(ms); tcp_proxy_set_enabled(p, true); } ================================================ FILE: tests/utils/tcp_proxy.h ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #ifndef TCP_PROXY_H #define TCP_PROXY_H #include #include typedef struct tcp_proxy tcp_proxy_t; // Create a proxy (not started). The proxy always binds to an ephemeral port. // listen_host may be NULL => "127.0.0.1" // target_host may be NULL => "127.0.0.1" tcp_proxy_t* tcp_proxy_create(const char* listen_host, const char* target_host, uint16_t target_port); // Start/stop the background proxy thread. // On success, returns the ephemeral TCP port bound on listen_host (>= 0). // On failure, returns a negative error code. int tcp_proxy_start(tcp_proxy_t* p, int ms_timeout); int tcp_proxy_stop(tcp_proxy_t* p); // Destroy the proxy (safe if already stopped). void tcp_proxy_destroy(tcp_proxy_t* p); // Enable/disable forwarding (true = forward, false = drop/blackhole). void tcp_proxy_set_enabled(tcp_proxy_t* p, bool enabled); bool tcp_proxy_get_enabled(const tcp_proxy_t* p); // Hard disconnect: close all client<->upstream pairs. void tcp_proxy_close_all(tcp_proxy_t* p); // Convenience: drop (blackhole) for ms, then restore to enabled = true. void tcp_proxy_drop_for_ms(tcp_proxy_t* p, int ms); #endif // TCP_PROXY_H ================================================ FILE: tests/valgrind.supp ================================================ # Suppress GLIBC TLS dtv allocation (false positive) { zenoh_pico_pthread_tls Memcheck:Leak match-leak-kinds: possible fun:calloc fun:calloc fun:allocate_dtv fun:_dl_allocate_tls fun:allocate_stack ... } ================================================ FILE: tests/z_api_admin_space_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #include "zenoh-pico/utils/string.h" #if Z_FEATURE_ADMIN_SPACE == 1 typedef struct admin_space_query_reply_t { z_owned_keyexpr_t ke; z_owned_string_t payload; z_owned_encoding_t encoding; } admin_space_query_reply_t; void admin_space_query_reply_clear(admin_space_query_reply_t *reply) { z_drop(z_move(reply->ke)); z_drop(z_move(reply->payload)); z_drop(z_move(reply->encoding)); } bool admin_space_query_reply_same_ke(const admin_space_query_reply_t *left, const admin_space_query_reply_t *right) { return z_keyexpr_equals(z_loan(left->ke), z_loan(right->ke)); } #if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif _Z_ELEM_DEFINE(admin_space_query_reply, admin_space_query_reply_t, _z_noop_size, admin_space_query_reply_clear, _z_noop_copy, _z_noop_move, admin_space_query_reply_same_ke, _z_noop_cmp, _z_noop_hash) _Z_LIST_DEFINE(admin_space_query_reply, admin_space_query_reply_t) static admin_space_query_reply_list_t *run_admin_space_query(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *query_ke) { admin_space_query_reply_list_t *results = admin_space_query_reply_list_new(); z_owned_closure_reply_t closure; z_owned_fifo_handler_reply_t handler; ASSERT_OK(z_fifo_channel_reply_new(&closure, &handler, 10)); z_get_options_t opts; z_get_options_default(&opts); opts.timeout_ms = 1000; ASSERT_OK(z_get(zs, query_ke, "", z_move(closure), &opts)); // Wait for replies to arrive z_time_t start = z_time_now(); for (unsigned long elapsed = z_time_elapsed_ms(&start);; elapsed = z_time_elapsed_ms(&start)) { /* Drain everything currently available */ z_owned_reply_t reply; z_result_t res; for (res = z_try_recv(z_loan(handler), &reply); res == _Z_RES_OK; res = z_try_recv(z_loan(handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { admin_space_query_reply_t *result = z_malloc(sizeof(*result)); ASSERT_NOT_NULL(result); const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); ASSERT_OK(z_bytes_to_string(z_sample_payload(sample), &result->payload)); ASSERT_OK(z_keyexpr_clone(&result->ke, z_sample_keyexpr(sample))); const z_loaned_encoding_t *encoding = z_sample_encoding(sample); ASSERT_OK(z_encoding_clone(&result->encoding, encoding)); ASSERT_TRUE(admin_space_query_reply_list_find(results, admin_space_query_reply_same_ke, result) == NULL); admin_space_query_reply_list_t *old = results; admin_space_query_reply_list_t *tmp = admin_space_query_reply_list_push(old, result); ASSERT_TRUE(tmp != old); results = tmp; } z_drop(z_move(reply)); } // If channel is closed, we're done regardless of timeout if (res == Z_CHANNEL_DISCONNECTED) { break; } // If nothing available right now, only stop once timeout elapsed if (res == Z_CHANNEL_NODATA) { if (elapsed >= opts.timeout_ms) { break; } z_sleep_ms(1); continue; } // Anything else is unexpected ASSERT_OK(res); } z_drop(z_move(handler)); return results; } typedef struct admin_space_test_keyexprs_t { z_owned_keyexpr_t pico_ke; z_owned_keyexpr_t session_ke; z_owned_keyexpr_t transports_ke; z_owned_keyexpr_t transport_0_ke; z_owned_keyexpr_t peers_ke; } admin_space_test_keyexprs_t; typedef struct admin_space_test_sessions_t { z_owned_session_t s1; z_owned_session_t s2; } admin_space_test_sessions_t; static void admin_space_test_sessions_open(admin_space_test_sessions_t *ss) { z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); ASSERT_OK(z_open(&ss->s1, z_move(c1), NULL)); ASSERT_OK(zp_start_admin_space(z_loan_mut(ss->s1))); ASSERT_OK(z_open(&ss->s2, z_move(c2), NULL)); z_sleep_ms(250); } static void admin_space_test_sessions_close(admin_space_test_sessions_t *ss) { ASSERT_OK(zp_stop_admin_space(z_loan_mut(ss->s1))); z_drop(z_move(ss->s1)); z_drop(z_move(ss->s2)); } static void admin_space_test_keyexprs_clear(admin_space_test_keyexprs_t *kes) { z_drop(z_move(kes->peers_ke)); z_drop(z_move(kes->transport_0_ke)); z_drop(z_move(kes->transports_ke)); z_drop(z_move(kes->session_ke)); z_drop(z_move(kes->pico_ke)); } static void assert_json_object(const z_loaned_string_t *s) { const char *p = z_string_data(s); size_t n = z_string_len(s); ASSERT_TRUE(n >= 2); ASSERT_TRUE(p[0] == '{'); ASSERT_TRUE(p[n - 1] == '}'); } static void assert_json_array(const z_loaned_string_t *s) { const char *p = z_string_data(s); size_t n = z_string_len(s); ASSERT_TRUE(n >= 2); ASSERT_TRUE(p[0] == '['); ASSERT_TRUE(p[n - 1] == ']'); } static void assert_contains(const z_loaned_string_t *s, const char *needle) { const char *start = z_string_data(s); const char *end = start + z_string_len(s); if (_z_strstr(start, end, needle) == NULL) { fprintf(stderr, "Assertion failed: expected substring not found.\nActual: %.*s\nNeedle: %s\n", (int)(end - start), start, needle); } ASSERT_TRUE(_z_strstr(start, end, needle) != NULL); } static void assert_not_contains(const z_loaned_string_t *s, const char *needle) { const char *start = z_string_data(s); const char *end = start + z_string_len(s); if (_z_strstr(start, end, needle) != NULL) { fprintf(stderr, "Assertion failed: unexpected substring found.\nActual: %.*s\nNeedle: %s\n", (int)(end - start), start, needle); } ASSERT_TRUE(_z_strstr(start, end, needle) == NULL); } static void assert_contains_z_string(const z_loaned_string_t *haystack, const z_loaned_string_t *needle) { const char *h_start = z_string_data(haystack); const char *h_end = h_start + z_string_len(haystack); size_t n = z_string_len(needle); ASSERT_TRUE(n < 256); char buf[256]; // Flawfinder: ignore [CWE-120] - checked by assert above memcpy(buf, z_string_data(needle), n); buf[n] = '\0'; ASSERT_TRUE(_z_strstr(h_start, h_end, buf) != NULL); } static void assert_contains_quoted_value(const z_loaned_string_t *s, const char *key, const char *val) { // e.g. key="mode" val="peer" -> "\"mode\":\"peer\"" char buf[128]; int n = snprintf(buf, sizeof(buf), "\"%s\":\"%s\"", key, val); ASSERT_TRUE(n > 0 && (size_t)n < sizeof(buf)); assert_contains(s, buf); } static const char *whatami_to_str(z_whatami_t w) { switch (w) { case Z_WHATAMI_ROUTER: return "router"; case Z_WHATAMI_PEER: return "peer"; case Z_WHATAMI_CLIENT: return "client"; default: return NULL; } } static const char *link_type_to_str(int t) { switch (t) { case _Z_LINK_TYPE_TCP: return "tcp"; case _Z_LINK_TYPE_UDP: return "udp"; case _Z_LINK_TYPE_BT: return "bt"; case _Z_LINK_TYPE_SERIAL: return "serial"; case _Z_LINK_TYPE_WS: return "ws"; case _Z_LINK_TYPE_TLS: return "tls"; case _Z_LINK_TYPE_RAWETH: return "raweth"; default: return NULL; } } static const char *cap_transport_to_str(int t) { switch (t) { case Z_LINK_CAP_TRANSPORT_UNICAST: return "unicast"; case Z_LINK_CAP_TRANSPORT_MULTICAST: return "multicast"; case Z_LINK_CAP_TRANSPORT_RAWETH: return "raweth"; default: return NULL; } } static const char *cap_flow_to_str(int f) { switch (f) { case Z_LINK_CAP_FLOW_DATAGRAM: return "datagram"; case Z_LINK_CAP_FLOW_STREAM: return "stream"; default: return NULL; } } static const char *transport_type_to_str(int t) { switch (t) { case _Z_TRANSPORT_UNICAST_TYPE: return "unicast"; case _Z_TRANSPORT_MULTICAST_TYPE: return "multicast"; case _Z_TRANSPORT_RAWETH_TYPE: return "raweth"; default: return NULL; } } static const _z_link_t *admin_space_test_transport_link(const _z_session_t *session) { switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: return session->_tp._transport._unicast._common._link; case _Z_TRANSPORT_MULTICAST_TYPE: return session->_tp._transport._multicast._common._link; case _Z_TRANSPORT_RAWETH_TYPE: return session->_tp._transport._raweth._common._link; default: ASSERT_TRUE(false); return NULL; } } static void assert_contains_session_header(const z_loaned_string_t *payload, const z_id_t *zid, z_whatami_t whatami, bool wrapped) { z_owned_string_t zid_str; ASSERT_OK(z_id_to_string(zid, &zid_str)); const char *whatami_str = whatami_to_str(whatami); ASSERT_NOT_NULL(whatami_str); char buf[256]; int n; if (wrapped) { n = snprintf(buf, sizeof(buf), "\"session\":{\"zid\":\"%.*s\",\"whatami\":\"%s\"", (int)z_string_len(z_loan(zid_str)), z_string_data(z_loan(zid_str)), whatami_str); } else { n = snprintf(buf, sizeof(buf), "{\"zid\":\"%.*s\",\"whatami\":\"%s\"", (int)z_string_len(z_loan(zid_str)), z_string_data(z_loan(zid_str)), whatami_str); } ASSERT_TRUE(n > 0 && (size_t)n < sizeof(buf)); assert_contains(payload, buf); z_drop(z_move(zid_str)); } static void assert_contains_transport_header(const z_loaned_string_t *payload, const char *transport_type) { char buf[128]; int n = snprintf(buf, sizeof(buf), "{\"type\":\"%s\",\"link\":", transport_type); ASSERT_TRUE(n > 0 && (size_t)n < sizeof(buf)); assert_contains(payload, buf); } static void assert_contains_link_header(const z_loaned_string_t *payload, const char *link_type) { char buf[128]; int n = snprintf(buf, sizeof(buf), "\"link\":{\"type\":\"%s\",\"endpoint\":", link_type); ASSERT_TRUE(n > 0 && (size_t)n < sizeof(buf)); assert_contains(payload, buf); } static void assert_contains_peer_header(const z_loaned_string_t *payload, const z_id_t *zid, z_whatami_t whatami, bool expect_remote_addr) { z_owned_string_t zid_str; ASSERT_OK(z_id_to_string(zid, &zid_str)); const char *whatami_str = whatami_to_str(whatami); ASSERT_NOT_NULL(whatami_str); char buf[256]; int n; if (expect_remote_addr) { n = snprintf(buf, sizeof(buf), "{\"zid\":\"%.*s\",\"whatami\":\"%s\",\"remote_addr\":", (int)z_string_len(z_loan(zid_str)), z_string_data(z_loan(zid_str)), whatami_str); } else { n = snprintf(buf, sizeof(buf), "{\"zid\":\"%.*s\",\"whatami\":\"%s\"", (int)z_string_len(z_loan(zid_str)), z_string_data(z_loan(zid_str)), whatami_str); } ASSERT_TRUE(n > 0 && (size_t)n < sizeof(buf)); assert_contains(payload, buf); z_drop(z_move(zid_str)); } static void build_pico_ke(z_owned_keyexpr_t *out, const z_id_t *zid) { z_owned_string_t s; ASSERT_OK(z_id_to_string(zid, &s)); z_internal_keyexpr_null(out); ASSERT_OK(_z_keyexpr_append_str(out, _Z_KEYEXPR_AT)); ASSERT_OK(_z_keyexpr_append_substr(out, z_string_data(z_loan(s)), z_string_len(z_loan(s)))); ASSERT_OK(_z_keyexpr_append_str(out, _Z_KEYEXPR_PICO)); z_drop(z_move(s)); } static void build_pico_session_ke(z_owned_keyexpr_t *out, const z_loaned_keyexpr_t *pico_ke) { ASSERT_OK(z_keyexpr_clone(out, pico_ke)); ASSERT_OK(_z_keyexpr_append_str(out, _Z_KEYEXPR_SESSION)); } static void build_pico_transports_ke(z_owned_keyexpr_t *out, const z_loaned_keyexpr_t *session_ke) { ASSERT_OK(z_keyexpr_clone(out, session_ke)); ASSERT_OK(_z_keyexpr_append_str(out, _Z_KEYEXPR_TRANSPORTS)); } static void build_pico_transport_0_ke(z_owned_keyexpr_t *out, const z_loaned_keyexpr_t *transports_ke) { ASSERT_OK(z_keyexpr_clone(out, transports_ke)); ASSERT_OK(_z_keyexpr_append_str(out, "0")); } static void build_pico_transport_0_peers_ke(z_owned_keyexpr_t *out, const z_loaned_keyexpr_t *transport_0_ke) { ASSERT_OK(z_keyexpr_clone(out, transport_0_ke)); ASSERT_OK(_z_keyexpr_append_str(out, _Z_KEYEXPR_PEERS)); } static void build_pico_transport_0_peer_ke(z_owned_keyexpr_t *out, const z_loaned_keyexpr_t *peers_ke, const z_id_t *peer_zid) { z_owned_string_t s; ASSERT_OK(z_id_to_string(peer_zid, &s)); ASSERT_OK(z_keyexpr_clone(out, peers_ke)); ASSERT_OK(_z_keyexpr_append_substr(out, z_string_data(z_loan(s)), z_string_len(z_loan(s)))); z_drop(z_move(s)); } static const admin_space_query_reply_t *find_expected_reply(const admin_space_query_reply_list_t *results, const z_loaned_keyexpr_t *expected_ke) { admin_space_query_reply_t expected; z_internal_keyexpr_null(&expected.ke); z_internal_string_null(&expected.payload); z_internal_encoding_null(&expected.encoding); ASSERT_OK(z_keyexpr_clone(&expected.ke, expected_ke)); admin_space_query_reply_list_t *entry = admin_space_query_reply_list_find(results, admin_space_query_reply_same_ke, &expected); admin_space_query_reply_elem_clear(&expected); ASSERT_NOT_NULL(entry); const admin_space_query_reply_t *reply = admin_space_query_reply_list_value(entry); ASSERT_NOT_NULL(reply); ASSERT_TRUE(z_encoding_equals(z_loan(reply->encoding), z_encoding_application_json())); return reply; } static const admin_space_query_reply_t *run_exact_admin_space_query(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *ke, admin_space_query_reply_list_t **results_out) { admin_space_query_reply_list_t *results = run_admin_space_query(zs, ke); ASSERT_TRUE(admin_space_query_reply_list_len(results) == 1); const admin_space_query_reply_t *reply = find_expected_reply(results, ke); ASSERT_NOT_NULL(reply); *results_out = results; return reply; } static void admin_space_test_keyexprs_init(admin_space_test_keyexprs_t *kes, const z_id_t *zid) { z_internal_keyexpr_null(&kes->pico_ke); z_internal_keyexpr_null(&kes->session_ke); z_internal_keyexpr_null(&kes->transports_ke); z_internal_keyexpr_null(&kes->transport_0_ke); z_internal_keyexpr_null(&kes->peers_ke); build_pico_ke(&kes->pico_ke, zid); build_pico_session_ke(&kes->session_ke, z_loan(kes->pico_ke)); build_pico_transports_ke(&kes->transports_ke, z_loan(kes->session_ke)); build_pico_transport_0_ke(&kes->transport_0_ke, z_loan(kes->transports_ke)); build_pico_transport_0_peers_ke(&kes->peers_ke, z_loan(kes->transport_0_ke)); } static size_t expected_admin_space_reply_count(const _z_session_t *session) { size_t count = 5; // pico, session, transports, transports/0, transports/0/peers switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: count += _z_transport_peer_unicast_slist_len(session->_tp._transport._unicast._peers); break; case _Z_TRANSPORT_MULTICAST_TYPE: count += _z_transport_peer_multicast_slist_len(session->_tp._transport._multicast._peers); break; case _Z_TRANSPORT_RAWETH_TYPE: count += _z_transport_peer_multicast_slist_len(session->_tp._transport._raweth._peers); break; default: break; } return count; } static void verify_peer_json(const z_loaned_string_t *payload, const z_id_t *expected_zid, z_whatami_t expected_whatami, bool expect_remote_addr) { assert_json_object(payload); assert_contains_peer_header(payload, expected_zid, expected_whatami, expect_remote_addr); } static void verify_peers_array_json_unicast(const z_loaned_string_t *payload, const _z_transport_unicast_t *tp) { assert_json_array(payload); for (_z_transport_peer_unicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_unicast_slist_next(it)) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(it); assert_contains_peer_header(payload, &peer->common._remote_zid, peer->common._remote_whatami, false); } } static void verify_peers_array_json_multicast(const z_loaned_string_t *payload, const _z_transport_multicast_t *tp) { assert_json_array(payload); for (_z_transport_peer_multicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_multicast_slist_next(it)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); assert_contains_peer_header(payload, &peer->common._remote_zid, peer->common._remote_whatami, true); } } static void verify_transport_json(const z_loaned_string_t *payload, int transport_type, const _z_link_t *link) { assert_contains(payload, "\"link\""); assert_contains(payload, "\"peers\""); const char *tt = transport_type_to_str(transport_type); ASSERT_NOT_NULL(tt); assert_contains_transport_header(payload, tt); const char *lt = link_type_to_str(link->_type); ASSERT_NOT_NULL(lt); assert_contains_link_header(payload, lt); assert_contains(payload, "\"endpoint\""); assert_contains(payload, "\"locator\""); assert_contains(payload, "\"metadata\":{"); assert_contains(payload, "\"protocol\""); assert_contains(payload, "\"address\""); assert_contains(payload, "\"config\":{"); assert_contains_z_string(payload, &link->_endpoint._locator._protocol); assert_contains_z_string(payload, &link->_endpoint._locator._address); assert_contains(payload, "\"capabilities\""); assert_contains(payload, "\"transport\""); assert_contains(payload, "\"flow\""); assert_contains(payload, "\"is_reliable\""); const char *ct = cap_transport_to_str(link->_cap._transport); const char *cf = cap_flow_to_str(link->_cap._flow); ASSERT_NOT_NULL(ct); ASSERT_NOT_NULL(cf); assert_contains_quoted_value(payload, "transport", ct); assert_contains_quoted_value(payload, "flow", cf); assert_contains(payload, link->_cap._is_reliable ? "\"is_reliable\":true" : "\"is_reliable\":false"); } static void verify_session_json(const z_loaned_string_t *payload, const _z_session_t *session) { assert_json_object(payload); assert_contains(payload, "\"transports\""); assert_contains_session_header(payload, &session->_local_zid, session->_mode, false); } static void verify_pico_json(const z_loaned_string_t *payload, const _z_session_t *session) { assert_json_object(payload); assert_contains(payload, "\"session\""); assert_contains(payload, "\"transports\""); assert_contains_session_header(payload, &session->_local_zid, session->_mode, true); } static void verify_admin_space_query(const z_loaned_session_t *zs, const admin_space_query_reply_list_t *results) { const _z_session_t *session = _Z_RC_IN_VAL(zs); const admin_space_query_reply_t *reply; const z_loaned_string_t *payload; z_owned_keyexpr_t pico_ke, session_ke, transports_ke, transport_0_ke, peers_ke; z_internal_keyexpr_null(&pico_ke); z_internal_keyexpr_null(&session_ke); z_internal_keyexpr_null(&transports_ke); z_internal_keyexpr_null(&transport_0_ke); z_internal_keyexpr_null(&peers_ke); build_pico_ke(&pico_ke, &session->_local_zid); build_pico_session_ke(&session_ke, z_loan(pico_ke)); build_pico_transports_ke(&transports_ke, z_loan(session_ke)); build_pico_transport_0_ke(&transport_0_ke, z_loan(transports_ke)); build_pico_transport_0_peers_ke(&peers_ke, z_loan(transport_0_ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) == expected_admin_space_reply_count(session)); reply = find_expected_reply(results, z_loan(pico_ke)); verify_pico_json(z_string_loan(&reply->payload), session); reply = find_expected_reply(results, z_loan(session_ke)); verify_session_json(z_string_loan(&reply->payload), session); reply = find_expected_reply(results, z_loan(transports_ke)); payload = z_string_loan(&reply->payload); assert_json_array(payload); verify_transport_json(payload, session->_tp._type, admin_space_test_transport_link(session)); reply = find_expected_reply(results, z_loan(transport_0_ke)); payload = z_string_loan(&reply->payload); assert_json_object(payload); verify_transport_json(payload, session->_tp._type, admin_space_test_transport_link(session)); reply = find_expected_reply(results, z_loan(peers_ke)); payload = z_string_loan(&reply->payload); switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: verify_peers_array_json_unicast(payload, &session->_tp._transport._unicast); break; case _Z_TRANSPORT_MULTICAST_TYPE: verify_peers_array_json_multicast(payload, &session->_tp._transport._multicast); break; case _Z_TRANSPORT_RAWETH_TYPE: verify_peers_array_json_multicast(payload, &session->_tp._transport._raweth); break; default: ASSERT_TRUE(false); } switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: { const _z_transport_unicast_t *tp = &session->_tp._transport._unicast; for (_z_transport_peer_unicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_unicast_slist_next(it)) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(peers_ke), &peer->common._remote_zid); reply = find_expected_reply(results, z_loan(peer_ke)); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, false); z_drop(z_move(peer_ke)); } break; } case _Z_TRANSPORT_MULTICAST_TYPE: { const _z_transport_multicast_t *tp = &session->_tp._transport._multicast; for (_z_transport_peer_multicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_multicast_slist_next(it)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(peers_ke), &peer->common._remote_zid); reply = find_expected_reply(results, z_loan(peer_ke)); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, true); z_drop(z_move(peer_ke)); } break; } case _Z_TRANSPORT_RAWETH_TYPE: { const _z_transport_multicast_t *tp = &session->_tp._transport._raweth; for (_z_transport_peer_multicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_multicast_slist_next(it)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(peers_ke), &peer->common._remote_zid); reply = find_expected_reply(results, z_loan(peer_ke)); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, true); z_drop(z_move(peer_ke)); } break; } default: ASSERT_TRUE(false); } z_drop(z_move(peers_ke)); z_drop(z_move(transport_0_ke)); z_drop(z_move(transports_ke)); z_drop(z_move(session_ke)); z_drop(z_move(pico_ke)); } void test_start_stop_admin_space(void) { printf("test_start_stop_admin_space\n"); z_owned_session_t s; z_owned_config_t c; z_config_default(&c); ASSERT_OK(z_open(&s, z_move(c), NULL)); ASSERT_EQ_U32(_Z_RC_IN_VAL(z_loan(s))->_admin_space_queryable_id, 0); for (int i = 0; i < 2; i++) { ASSERT_OK(zp_start_admin_space(z_loan_mut(s))); ASSERT_TRUE(_Z_RC_IN_VAL(z_loan(s))->_admin_space_queryable_id > 0); ASSERT_OK(zp_stop_admin_space(z_loan_mut(s))); ASSERT_EQ_U32(_Z_RC_IN_VAL(z_loan(s))->_admin_space_queryable_id, 0); } z_drop(z_move(s)); } void test_auto_start_admin_space(void) { printf("test_auto_start_admin_space\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_open_options_t opt1, opt2; z_open_options_default(&opt1); z_open_options_default(&opt2); opt2.auto_start_admin_space = true; ASSERT_OK(z_open(&s1, z_move(c1), &opt1)); ASSERT_EQ_U32(_Z_RC_IN_VAL(z_loan(s1))->_admin_space_queryable_id, 0); z_drop(z_move(s1)); ASSERT_OK(z_open(&s2, z_move(c2), &opt2)); ASSERT_TRUE(_Z_RC_IN_VAL(z_loan(s2))->_admin_space_queryable_id > 0); z_drop(z_move(s2)); } void test_admin_space_query_fails_when_not_running(void) { printf("test_admin_space_query_fails_when_not_running\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); ASSERT_OK(z_open(&s1, z_move(c1), NULL)); ASSERT_OK(z_open(&s2, z_move(c2), NULL)); // Wait for sessions to connect z_sleep_ms(250); // Build query keyexpr: @//pico/** z_id_t zid = z_info_zid(z_loan(s1)); z_owned_string_t zid_str; ASSERT_OK(z_id_to_string(&zid, &zid_str)); z_owned_keyexpr_t ke; z_internal_keyexpr_null(&ke); ASSERT_OK(_z_keyexpr_append_str(&ke, _Z_KEYEXPR_AT)); ASSERT_OK(_z_keyexpr_append_substr(&ke, z_string_data(z_loan(zid_str)), z_string_len(z_loan(zid_str)))); z_drop(z_move(zid_str)); ASSERT_OK(_z_keyexpr_append_str(&ke, _Z_KEYEXPR_PICO)); ASSERT_OK(_z_keyexpr_append_str(&ke, _Z_KEYEXPR_STARSTAR)); // 1) Not running - expect no replies admin_space_query_reply_list_t *results = run_admin_space_query(z_loan(s2), z_keyexpr_loan(&ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) == 0); admin_space_query_reply_list_free(&results); // 2) Start admin space - expect replies ASSERT_OK(zp_start_admin_space(z_loan_mut(s1))); z_sleep_ms(250); results = run_admin_space_query(z_loan(s2), z_keyexpr_loan(&ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) > 0); admin_space_query_reply_list_free(&results); // 3) Stop admin space - expect no replies again ASSERT_OK(zp_stop_admin_space(z_loan_mut(s1))); results = run_admin_space_query(z_loan(s2), z_keyexpr_loan(&ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) == 0); admin_space_query_reply_list_free(&results); z_keyexpr_drop(z_keyexpr_move(&ke)); z_drop(z_move(s1)); z_drop(z_move(s2)); } void test_admin_space_general_query_succeeds(void) { printf("test_admin_space_general_query_succeeds\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); ASSERT_OK(z_open(&s1, z_move(c1), NULL)); ASSERT_OK(zp_start_admin_space(z_loan_mut(s1))); ASSERT_OK(z_open(&s2, z_move(c2), NULL)); z_sleep_ms(250); z_id_t zid = z_info_zid(z_loan(s1)); z_owned_string_t zid_str; ASSERT_OK(z_id_to_string(&zid, &zid_str)); z_owned_keyexpr_t query_ke; z_internal_keyexpr_null(&query_ke); ASSERT_OK(_z_keyexpr_append_str(&query_ke, _Z_KEYEXPR_AT)); ASSERT_OK(_z_keyexpr_append_substr(&query_ke, z_string_data(z_loan(zid_str)), z_string_len(z_loan(zid_str)))); z_drop(z_move(zid_str)); ASSERT_OK(_z_keyexpr_append_str(&query_ke, _Z_KEYEXPR_PICO)); ASSERT_OK(_z_keyexpr_append_str(&query_ke, _Z_KEYEXPR_STARSTAR)); admin_space_query_reply_list_t *results = run_admin_space_query(z_loan(s2), z_loan(query_ke)); verify_admin_space_query(z_loan(s1), results); admin_space_query_reply_list_free(&results); z_drop(z_move(query_ke)); ASSERT_OK(zp_stop_admin_space(z_loan_mut(s1))); z_drop(z_move(s1)); z_drop(z_move(s2)); } void test_admin_space_pico_endpoint_succeeds(void) { printf("test_admin_space_pico_endpoint_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(kes.pico_ke), &results); verify_pico_json(z_string_loan(&reply->payload), session); admin_space_query_reply_list_free(&results); admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } void test_admin_space_session_endpoint_succeeds(void) { printf("test_admin_space_session_endpoint_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(kes.session_ke), &results); verify_session_json(z_string_loan(&reply->payload), session); admin_space_query_reply_list_free(&results); admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } void test_admin_space_transports_endpoint_succeeds(void) { printf("test_admin_space_transports_endpoint_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(kes.transports_ke), &results); const z_loaned_string_t *payload = z_string_loan(&reply->payload); assert_json_array(payload); verify_transport_json(payload, session->_tp._type, admin_space_test_transport_link(session)); admin_space_query_reply_list_free(&results); admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } void test_admin_space_transport_0_endpoint_succeeds(void) { printf("test_admin_space_transport_0_endpoint_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(kes.transport_0_ke), &results); const z_loaned_string_t *payload = z_string_loan(&reply->payload); assert_json_object(payload); verify_transport_json(payload, session->_tp._type, admin_space_test_transport_link(session)); admin_space_query_reply_list_free(&results); admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } void test_admin_space_transport_0_peers_endpoint_succeeds(void) { printf("test_admin_space_transport_0_peers_endpoint_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(kes.peers_ke), &results); const z_loaned_string_t *payload = z_string_loan(&reply->payload); switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: verify_peers_array_json_unicast(payload, &session->_tp._transport._unicast); break; case _Z_TRANSPORT_MULTICAST_TYPE: verify_peers_array_json_multicast(payload, &session->_tp._transport._multicast); break; case _Z_TRANSPORT_RAWETH_TYPE: verify_peers_array_json_multicast(payload, &session->_tp._transport._raweth); break; default: ASSERT_TRUE(false); } admin_space_query_reply_list_free(&results); admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } void test_admin_space_transport_0_peer_endpoints_succeeds(void) { printf("test_admin_space_transport_0_peer_endpoints_succeeds\n"); admin_space_test_sessions_t ss; admin_space_test_sessions_open(&ss); const _z_session_t *session = _Z_RC_IN_VAL(z_loan(ss.s1)); admin_space_test_keyexprs_t kes; admin_space_test_keyexprs_init(&kes, &session->_local_zid); switch (session->_tp._type) { case _Z_TRANSPORT_UNICAST_TYPE: { const _z_transport_unicast_t *tp = &session->_tp._transport._unicast; for (_z_transport_peer_unicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_unicast_slist_next(it)) { const _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(kes.peers_ke), &peer->common._remote_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(peer_ke), &results); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, false); admin_space_query_reply_list_free(&results); z_drop(z_move(peer_ke)); } break; } case _Z_TRANSPORT_MULTICAST_TYPE: { const _z_transport_multicast_t *tp = &session->_tp._transport._multicast; for (_z_transport_peer_multicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_multicast_slist_next(it)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(kes.peers_ke), &peer->common._remote_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(peer_ke), &results); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, true); admin_space_query_reply_list_free(&results); z_drop(z_move(peer_ke)); } break; } case _Z_TRANSPORT_RAWETH_TYPE: { const _z_transport_multicast_t *tp = &session->_tp._transport._raweth; for (_z_transport_peer_multicast_slist_t *it = tp->_peers; it != NULL; it = _z_transport_peer_multicast_slist_next(it)) { const _z_transport_peer_multicast_t *peer = _z_transport_peer_multicast_slist_value(it); z_owned_keyexpr_t peer_ke; z_internal_keyexpr_null(&peer_ke); build_pico_transport_0_peer_ke(&peer_ke, z_loan(kes.peers_ke), &peer->common._remote_zid); admin_space_query_reply_list_t *results; const admin_space_query_reply_t *reply = run_exact_admin_space_query(z_loan(ss.s2), z_loan(peer_ke), &results); verify_peer_json(z_string_loan(&reply->payload), &peer->common._remote_zid, peer->common._remote_whatami, true); admin_space_query_reply_list_free(&results); z_drop(z_move(peer_ke)); } break; } default: ASSERT_TRUE(false); } admin_space_test_keyexprs_clear(&kes); admin_space_test_sessions_close(&ss); } #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_LINK_TCP == 1 && \ Z_FEATURE_MULTI_THREAD == 1 && Z_FEATURE_PUBLICATION == 1 static void open_listener_session(z_owned_session_t *session, const char *listen_locator) { z_owned_config_t cfg; z_config_default(&cfg); ASSERT_OK(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_MODE_KEY, "peer")); ASSERT_OK(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_LISTEN_KEY, listen_locator)); ASSERT_OK(z_open(session, z_config_move(&cfg), NULL)); } static void open_connector_session(z_owned_session_t *session, const char *connect_locator) { z_owned_config_t cfg; z_config_default(&cfg); ASSERT_OK(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_MODE_KEY, "peer")); ASSERT_OK(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_CONNECT_KEY, connect_locator)); ASSERT_OK(z_open(session, z_config_move(&cfg), NULL)); } static void close_session_with_tasks(z_owned_session_t *session) { z_session_drop(z_session_move(session)); } static void wait_for_sample_kind(z_owned_fifo_handler_sample_t *handler, z_sample_kind_t expected_kind, z_owned_string_t *payload_out) { if (payload_out != NULL) { z_internal_string_null(payload_out); } for (unsigned i = 0; i < 50; ++i) { z_owned_sample_t sample; z_result_t res = z_fifo_handler_sample_try_recv(z_fifo_handler_sample_loan(handler), &sample); if (res == Z_CHANNEL_NODATA) { z_sleep_ms(100); continue; } ASSERT_OK(res); if (z_sample_kind(z_loan(sample)) == expected_kind) { if (payload_out != NULL && expected_kind == Z_SAMPLE_KIND_PUT) { ASSERT_OK(z_bytes_to_string(z_sample_payload(z_loan(sample)), payload_out)); } z_drop(z_move(sample)); return; } z_drop(z_move(sample)); } ASSERT_TRUE(false); } static void assert_no_sample(z_owned_fifo_handler_sample_t *handler) { for (unsigned i = 0; i < 5; ++i) { z_owned_sample_t sample; z_result_t res = z_fifo_handler_sample_try_recv(z_fifo_handler_sample_loan(handler), &sample); if (res == Z_CHANNEL_NODATA) { z_sleep_ms(100); continue; } if (res == _Z_RES_OK) { z_drop(z_move(sample)); } ASSERT_TRUE(false); } } static void wait_admin_space_ready(const z_loaned_session_t *zs, const z_loaned_keyexpr_t *probe_ke) { bool ready = false; for (unsigned i = 0; i < 50; ++i) { admin_space_query_reply_list_t *results = run_admin_space_query(zs, probe_ke); ready = admin_space_query_reply_list_len(results) > 0; admin_space_query_reply_list_free(&results); if (ready) { break; } z_sleep_ms(100); } ASSERT_TRUE(ready); } void test_admin_space_rfc_connectivity_query_and_events(void) { z_owned_session_t s1, s2, s3; open_listener_session(&s1, "tcp/127.0.0.1:7448"); ASSERT_OK(zp_start_admin_space(z_session_loan_mut(&s1))); open_connector_session(&s2, "tcp/127.0.0.1:7448"); z_id_t s1_zid = z_info_zid(z_session_loan(&s1)); z_owned_string_t s1_zid_str; ASSERT_OK(z_id_to_string(&s1_zid, &s1_zid_str)); char session_query_ke_str[256]; int session_query_ke_len = snprintf(session_query_ke_str, sizeof(session_query_ke_str), "@/%.*s/%s/**", (int)z_string_len(z_string_loan(&s1_zid_str)), z_string_data(z_string_loan(&s1_zid_str)), _Z_KEYEXPR_SESSION); ASSERT_TRUE(session_query_ke_len > 0 && (size_t)session_query_ke_len < sizeof(session_query_ke_str)); z_view_keyexpr_t session_query_ke; ASSERT_OK(z_view_keyexpr_from_str(&session_query_ke, session_query_ke_str)); wait_admin_space_ready(z_session_loan(&s1), z_view_keyexpr_loan(&session_query_ke)); admin_space_query_reply_list_t *results = run_admin_space_query(z_session_loan(&s2), z_view_keyexpr_loan(&session_query_ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) == 0); admin_space_query_reply_list_free(&results); results = run_admin_space_query(z_session_loan(&s1), z_view_keyexpr_loan(&session_query_ke)); ASSERT_TRUE(admin_space_query_reply_list_len(results) >= 2); bool saw_transport = false; bool saw_link = false; for (admin_space_query_reply_list_t *it = results; it != NULL; it = admin_space_query_reply_list_next(it)) { const admin_space_query_reply_t *reply = admin_space_query_reply_list_value(it); z_view_string_t key_view; ASSERT_OK(z_keyexpr_as_view_string(z_keyexpr_loan(&reply->ke), &key_view)); const z_loaned_string_t *key = z_view_string_loan(&key_view); const char *k_start = z_string_data(key); const char *k_end = k_start + z_string_len(key); if (_z_strstr(k_start, k_end, "/session/transport/unicast/") != NULL && _z_strstr(k_start, k_end, "/link/") == NULL) { saw_transport = true; assert_contains(z_string_loan(&reply->payload), "\"zid\""); assert_contains(z_string_loan(&reply->payload), "\"whatami\""); assert_contains(z_string_loan(&reply->payload), "\"is_qos\""); assert_contains(z_string_loan(&reply->payload), "\"is_shm\""); } if (_z_strstr(k_start, k_end, "/session/transport/unicast/") != NULL && _z_strstr(k_start, k_end, "/link/") != NULL) { saw_link = true; assert_contains(z_string_loan(&reply->payload), "\"src\""); assert_contains(z_string_loan(&reply->payload), "\"dst\""); assert_contains(z_string_loan(&reply->payload), "\"group\":null"); assert_contains(z_string_loan(&reply->payload), "\"mtu\""); assert_contains(z_string_loan(&reply->payload), "\"is_reliable\""); assert_contains(z_string_loan(&reply->payload), "\"is_streamed\""); assert_not_contains(z_string_loan(&reply->payload), "\"zid\""); } } ASSERT_TRUE(saw_transport); ASSERT_TRUE(saw_link); admin_space_query_reply_list_free(&results); char transport_sub_ke_str[256]; int transport_sub_ke_len = snprintf(transport_sub_ke_str, sizeof(transport_sub_ke_str), "@/%.*s/%s/%s/*", (int)z_string_len(z_string_loan(&s1_zid_str)), z_string_data(z_string_loan(&s1_zid_str)), _Z_KEYEXPR_SESSION, _Z_KEYEXPR_TRANSPORT_UNICAST); ASSERT_TRUE(transport_sub_ke_len > 0 && (size_t)transport_sub_ke_len < sizeof(transport_sub_ke_str)); char link_sub_ke_str[320]; int link_sub_ke_len = snprintf( link_sub_ke_str, sizeof(link_sub_ke_str), "@/%.*s/%s/%s/*/%s/*", (int)z_string_len(z_string_loan(&s1_zid_str)), z_string_data(z_string_loan(&s1_zid_str)), _Z_KEYEXPR_SESSION, _Z_KEYEXPR_TRANSPORT_UNICAST, _Z_KEYEXPR_LINK); ASSERT_TRUE(link_sub_ke_len > 0 && (size_t)link_sub_ke_len < sizeof(link_sub_ke_str)); z_drop(z_move(s1_zid_str)); z_view_keyexpr_t transport_sub_ke; z_view_keyexpr_t link_sub_ke; ASSERT_OK(z_view_keyexpr_from_str(&transport_sub_ke, transport_sub_ke_str)); ASSERT_OK(z_view_keyexpr_from_str(&link_sub_ke, link_sub_ke_str)); z_owned_closure_sample_t transport_sub_closure; z_owned_fifo_handler_sample_t transport_sub_handler; ASSERT_OK(z_fifo_channel_sample_new(&transport_sub_closure, &transport_sub_handler, 8)); z_owned_subscriber_t transport_sub; ASSERT_OK(z_declare_subscriber(z_session_loan(&s1), &transport_sub, z_view_keyexpr_loan(&transport_sub_ke), z_closure_sample_move(&transport_sub_closure), NULL)); z_owned_closure_sample_t link_sub_closure; z_owned_fifo_handler_sample_t link_sub_handler; ASSERT_OK(z_fifo_channel_sample_new(&link_sub_closure, &link_sub_handler, 8)); z_owned_subscriber_t link_sub; ASSERT_OK(z_declare_subscriber(z_session_loan(&s1), &link_sub, z_view_keyexpr_loan(&link_sub_ke), z_closure_sample_move(&link_sub_closure), NULL)); z_owned_closure_sample_t remote_transport_sub_closure; z_owned_fifo_handler_sample_t remote_transport_sub_handler; ASSERT_OK(z_fifo_channel_sample_new(&remote_transport_sub_closure, &remote_transport_sub_handler, 8)); z_owned_subscriber_t remote_transport_sub; ASSERT_OK(z_declare_subscriber(z_session_loan(&s2), &remote_transport_sub, z_view_keyexpr_loan(&transport_sub_ke), z_closure_sample_move(&remote_transport_sub_closure), NULL)); z_owned_closure_sample_t remote_link_sub_closure; z_owned_fifo_handler_sample_t remote_link_sub_handler; ASSERT_OK(z_fifo_channel_sample_new(&remote_link_sub_closure, &remote_link_sub_handler, 8)); z_owned_subscriber_t remote_link_sub; ASSERT_OK(z_declare_subscriber(z_session_loan(&s2), &remote_link_sub, z_view_keyexpr_loan(&link_sub_ke), z_closure_sample_move(&remote_link_sub_closure), NULL)); open_connector_session(&s3, "tcp/127.0.0.1:7448"); z_owned_string_t transport_put_payload; z_owned_string_t link_put_payload; wait_for_sample_kind(&transport_sub_handler, Z_SAMPLE_KIND_PUT, &transport_put_payload); wait_for_sample_kind(&link_sub_handler, Z_SAMPLE_KIND_PUT, &link_put_payload); assert_contains(z_string_loan(&transport_put_payload), "\"is_qos\""); assert_contains(z_string_loan(&transport_put_payload), "\"is_shm\""); assert_contains(z_string_loan(&link_put_payload), "\"group\":null"); assert_not_contains(z_string_loan(&link_put_payload), "\"zid\""); z_drop(z_move(transport_put_payload)); z_drop(z_move(link_put_payload)); assert_no_sample(&remote_transport_sub_handler); assert_no_sample(&remote_link_sub_handler); close_session_with_tasks(&s3); wait_for_sample_kind(&link_sub_handler, Z_SAMPLE_KIND_DELETE, NULL); wait_for_sample_kind(&transport_sub_handler, Z_SAMPLE_KIND_DELETE, NULL); assert_no_sample(&remote_transport_sub_handler); assert_no_sample(&remote_link_sub_handler); ASSERT_OK(z_undeclare_subscriber(z_subscriber_move(&transport_sub))); ASSERT_OK(z_undeclare_subscriber(z_subscriber_move(&link_sub))); ASSERT_OK(z_undeclare_subscriber(z_subscriber_move(&remote_transport_sub))); ASSERT_OK(z_undeclare_subscriber(z_subscriber_move(&remote_link_sub))); z_drop(z_move(transport_sub_handler)); z_drop(z_move(link_sub_handler)); z_drop(z_move(remote_transport_sub_handler)); z_drop(z_move(remote_link_sub_handler)); ASSERT_OK(zp_stop_admin_space(z_session_loan_mut(&s1))); close_session_with_tasks(&s2); close_session_with_tasks(&s1); } #endif int main(int argc, char **argv) { (void)argc; (void)argv; test_start_stop_admin_space(); test_auto_start_admin_space(); test_admin_space_query_fails_when_not_running(); test_admin_space_general_query_succeeds(); test_admin_space_pico_endpoint_succeeds(); test_admin_space_session_endpoint_succeeds(); test_admin_space_transports_endpoint_succeeds(); test_admin_space_transport_0_endpoint_succeeds(); test_admin_space_transport_0_peers_endpoint_succeeds(); test_admin_space_transport_0_peer_endpoints_succeeds(); #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_LINK_TCP == 1 && \ Z_FEATURE_MULTI_THREAD == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_LOCAL_QUERYABLE == 1 && \ Z_FEATURE_LOCAL_SUBSCRIBER == 1 test_admin_space_rfc_connectivity_query_and_events(); #endif return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf("Missing config token to build this test. This test requires: Z_FEATURE_ADMIN_SPACE\n"); return 0; } #endif // Z_FEATURE_ADMIN_SPACE == 1 ================================================ FILE: tests/z_api_advanced_pubsub_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #ifdef Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY #include "utils/tcp_proxy.h" #endif #if Z_FEATURE_ADVANCED_PUBLICATION == 1 && Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 // ---- Common test timing constants ---- #define TEST_SLEEP_MS 4000 // Reconnect waits used across tests #define TEST_RECONNECT_MS 10000 // Feature-specific periods used in tests #define TEST_PERIODIC_QUERY_MS 1000 #define TEST_HEARTBEAT_PERIOD_MS 4000 // ------------------------------------------------------------ static void put_str(const ze_loaned_advanced_publisher_t *pub, const char *s) { z_owned_bytes_t payload; ASSERT_OK(z_bytes_copy_from_str(&payload, s)); ASSERT_OK(ze_advanced_publisher_put(pub, z_move(payload), NULL)); // TODO: cache requires monotonically increasing timestamps to work correctly on advanced subscriber side. // Due to currently imprecise timestamping (due to low resolution of gettimeofday), we need // to add a small delay to ensure that we get monotonically increasing timestamps, until // more accurate timestamping (hlc ?) is implemented. z_sleep_us(50); } static void expect_next(const z_loaned_fifo_handler_sample_t *handler, const char *expected) { z_owned_sample_t sample; z_internal_sample_null(&sample); z_owned_string_t value; z_internal_string_null(&value); ASSERT_OK(z_try_recv(handler, &sample)); assert(z_sample_kind(z_loan(sample)) == Z_SAMPLE_KIND_PUT); ASSERT_OK(z_bytes_to_string(z_sample_payload(z_loan(sample)), &value)); const char *ptr = z_string_data(z_loan(value)); size_t len = z_string_len(z_loan(value)); // Flawfinder: ignore [CWE-126] size_t exp_len = strlen(expected); if (len != exp_len || memcmp(ptr, expected, len) != 0) { fprintf(stderr, "Mismatch:\n" " expected (%zu): \"%.*s\"\n" " actual (%zu): \"%.*s\"\n", exp_len, (int)exp_len, expected, len, (int)len, ptr); fflush(stderr); assert(false && "expect_next() value mismatch"); } z_drop(z_move(value)); z_drop(z_move(sample)); } static void expect_empty(const z_loaned_fifo_handler_sample_t *handler) { z_owned_sample_t sample; ASSERT_NOT_OK(z_try_recv(handler, &sample)); } static void test_advanced_history(bool p2p) { printf("test_advanced_history: peer to peer=%d\n", p2p); const char *expr = "zenoh-pico/advanced-pubsub/test/history"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); if (p2p) { zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); } ASSERT_OK(z_open(&s1, z_config_move(&c1), NULL)); ASSERT_OK(z_open(&s2, z_config_move(&c2), NULL)); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); pub_opts.cache.max_samples = 3; pub_opts.cache.is_enabled = true; ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); char buf[16]; for (int idx = 1; idx <= 4; idx++) { snprintf(buf, sizeof(buf), "%d", idx); put_str(z_loan(pub), buf); } z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_history_options_default(&sub_opts.history); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "5"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "2"); expect_next(z_loan(handler), "3"); expect_next(z_loan(handler), "4"); expect_next(z_loan(handler), "5"); expect_empty(z_loan(handler)); ze_advanced_subscriber_drop(z_move(sub)); ze_advanced_publisher_drop(z_move(pub)); z_fifo_handler_sample_drop(z_move(handler)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #ifdef Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY static void setup_two_peers_with_proxy(z_owned_session_t *s1, z_owned_session_t *s2, tcp_proxy_t **proxy, uint16_t upstream_listen_port) { *proxy = tcp_proxy_create("127.0.0.1", "127.0.0.1", upstream_listen_port); assert(*proxy != NULL); int port = tcp_proxy_start(*proxy, 100); assert(port > 0); z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); char uri[128]; // s1 listens on the fixed upstream port zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); snprintf(uri, sizeof(uri), "tcp/127.0.0.1:%u#iface=lo", (unsigned)upstream_listen_port); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, uri); // s2 connects via proxy ephemeral port zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); snprintf(uri, sizeof(uri), "tcp/127.0.0.1:%u#iface=lo", (unsigned)port); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, uri); ASSERT_OK(z_open(s1, z_config_move(&c1), NULL)); ASSERT_OK(z_open(s2, z_config_move(&c2), NULL)); } static void test_advanced_retransmission(void) { printf("test_advanced_retransmission\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/retransmission"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 10; ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "2"); put_str(z_loan(pub), "3"); put_str(z_loan(pub), "4"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); put_str(z_loan(pub), "5"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "2"); expect_next(z_loan(handler), "3"); expect_next(z_loan(handler), "4"); expect_next(z_loan(handler), "5"); expect_empty(z_loan(handler)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } static void test_advanced_retransmission_periodic(void) { printf("test_advanced_retransmission_periodic\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/retransmission_periodic"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); ze_advanced_subscriber_last_sample_miss_detection_options_default(&sub_opts.recovery.last_sample_miss_detection); sub_opts.recovery.last_sample_miss_detection.periodic_queries_period_ms = TEST_PERIODIC_QUERY_MS; z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 10; ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "2"); put_str(z_loan(pub), "3"); put_str(z_loan(pub), "4"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); put_str(z_loan(pub), "5"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "2"); expect_next(z_loan(handler), "3"); expect_next(z_loan(handler), "4"); expect_next(z_loan(handler), "5"); expect_empty(z_loan(handler)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } static void test_advanced_retransmission_heartbeat(void) { printf("test_advanced_retransmission_heartbeat\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/retransmission_heartbeat"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); ze_advanced_subscriber_last_sample_miss_detection_options_default(&sub_opts.recovery.last_sample_miss_detection); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 10; ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); pub_opts.sample_miss_detection.heartbeat_mode = ZE_ADVANCED_PUBLISHER_HEARTBEAT_MODE_PERIODIC; pub_opts.sample_miss_detection.heartbeat_period_ms = TEST_HEARTBEAT_PERIOD_MS; ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "2"); put_str(z_loan(pub), "3"); put_str(z_loan(pub), "4"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); expect_next(z_loan(handler), "2"); expect_next(z_loan(handler), "3"); expect_next(z_loan(handler), "4"); expect_empty(z_loan(handler)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } #define MAX_MISS_EVENTS 16 typedef struct { atomic_int event_count; // number of miss events recorded atomic_uint_fast64_t total_nb; // sum of all nb across events z_entity_global_id_t sources[MAX_MISS_EVENTS]; uint64_t nbs[MAX_MISS_EVENTS]; } miss_ctx_t; static void miss_ctx_init(miss_ctx_t *ctx) { memset(ctx, 0, sizeof(*ctx)); atomic_store_explicit(&ctx->event_count, 0, memory_order_relaxed); atomic_store_explicit(&ctx->total_nb, 0, memory_order_relaxed); } static void miss_ctx_assert_single(const miss_ctx_t *ctx, const _z_entity_global_id_t *expected_pub_id, uint64_t expected_nb) { int count = atomic_load_explicit(&ctx->event_count, memory_order_acquire); uint64_t sum = atomic_load_explicit(&ctx->total_nb, memory_order_acquire); if (count != 1) { fprintf(stderr, "miss_ctx_assert_single: expected exactly 1 miss event, got %d\n", count); fflush(stderr); assert(false && "expected exactly one miss event"); } if (sum != expected_nb) { fprintf(stderr, "total missed samples mismatch: expected=%" PRIu64 ", actual=%" PRIu64 "\n", expected_nb, sum); fflush(stderr); assert(false && "total missed samples does not match"); } if (ctx->nbs[0] != expected_nb) { fprintf(stderr, "single-event nb mismatch: expected=%" PRIu64 ", actual=%" PRIu64 "\n", expected_nb, ctx->nbs[0]); fflush(stderr); assert(false && "unexpected nb in the single miss event"); } if (!_z_entity_global_id_eq(&ctx->sources[0], expected_pub_id)) { z_owned_string_t id_string, expected_id_string; ASSERT_OK(z_id_to_string(&ctx->sources[0].zid, &id_string)); ASSERT_OK(z_id_to_string(&expected_pub_id->zid, &expected_id_string)); fprintf(stderr, "miss source GID mismatch:\n" " expected: (zid=%.*s, eid=%d)\n" " actual : (zid=%.*s, eid=%d)\n", (int)z_string_len(z_loan(expected_id_string)), z_string_data(z_loan(expected_id_string)), expected_pub_id->eid, (int)z_string_len(z_loan(id_string)), z_string_data(z_loan(id_string)), ctx->sources[0].eid); fflush(stderr); z_drop(z_move(id_string)); z_drop(z_move(expected_id_string)); assert(false && "miss source should match publisher"); } } static void miss_handler(const ze_miss_t *miss, void *arg) { miss_ctx_t *ctx = (miss_ctx_t *)arg; int idx = atomic_load_explicit(&ctx->event_count, memory_order_relaxed); assert(idx < MAX_MISS_EVENTS); ctx->nbs[idx] = miss->nb; ctx->sources[idx] = miss->source; atomic_fetch_add_explicit(&ctx->total_nb, miss->nb, memory_order_relaxed); atomic_store_explicit(&ctx->event_count, idx + 1, memory_order_release); } static void test_advanced_sample_miss(void) { printf("test_advanced_sample_miss\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/sample_miss"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); ze_owned_sample_miss_listener_t miss_listener; ze_owned_closure_miss_t miss_closure; miss_ctx_t miss_ctx; miss_ctx_init(&miss_ctx); z_closure(&miss_closure, miss_handler, NULL, &miss_ctx); ASSERT_OK(ze_advanced_subscriber_declare_sample_miss_listener(z_loan(sub), &miss_listener, z_move(miss_closure))); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "2"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); put_str(z_loan(pub), "3"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "3"); expect_empty(z_loan(handler)); z_entity_global_id_t pub_id = ze_advanced_publisher_id(z_loan(pub)); miss_ctx_assert_single(&miss_ctx, &pub_id, 1); z_drop(z_move(miss_listener)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } static void test_advanced_retransmission_sample_miss(void) { printf("test_advanced_retransmission_sample_miss\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/retransmission_sample_miss"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); ze_advanced_subscriber_last_sample_miss_detection_options_default(&sub_opts.recovery.last_sample_miss_detection); sub_opts.recovery.last_sample_miss_detection.periodic_queries_period_ms = TEST_PERIODIC_QUERY_MS; z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); ze_owned_sample_miss_listener_t miss_listener; ze_owned_closure_miss_t miss_closure; miss_ctx_t miss_ctx; miss_ctx_init(&miss_ctx); z_closure(&miss_closure, miss_handler, NULL, &miss_ctx); ASSERT_OK(ze_advanced_subscriber_declare_sample_miss_listener(z_loan(sub), &miss_listener, z_move(miss_closure))); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 1; ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "2"); put_str(z_loan(pub), "3"); put_str(z_loan(pub), "4"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); expect_next(z_loan(handler), "4"); expect_empty(z_loan(handler)); z_entity_global_id_t pub_id = ze_advanced_publisher_id(z_loan(pub)); miss_ctx_assert_single(&miss_ctx, &pub_id, 2); z_drop(z_move(miss_listener)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } /* TODO: Test disabled due to session reconnection issues static void test_advanced_late_joiner(void) { printf("test_advanced_late_joiner\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/late_joiner"; tcp_proxy_t *tcp_proxy; z_owned_session_t s1, s2; setup_two_peers_with_proxy(&s1, &s2, &tcp_proxy, 9000); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_history_options_default(&sub_opts.history); sub_opts.history.detect_late_publishers = true; z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 10)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s2), &sub, z_loan(k), z_move(closure), &sub_opts)); z_sleep_ms(TEST_SLEEP_MS); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(TEST_SLEEP_MS); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 10; pub_opts.publisher_detection = true; ASSERT_OK(ze_declare_advanced_publisher(z_loan(s1), &pub, z_loan(k), &pub_opts)); put_str(z_loan(pub), "1"); put_str(z_loan(pub), "2"); put_str(z_loan(pub), "3"); z_sleep_ms(TEST_SLEEP_MS); expect_empty(z_loan(handler)); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_ms(TEST_RECONNECT_MS); put_str(z_loan(pub), "4"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_next(z_loan(handler), "2"); expect_next(z_loan(handler), "3"); expect_next(z_loan(handler), "4"); expect_empty(z_loan(handler)); z_drop(z_move(sub)); z_drop(z_move(pub)); z_drop(z_move(handler)); z_drop(z_move(s1)); z_drop(z_move(s2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); }*/ #endif // Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 && Z_FEATURE_LOCAL_QUERYABLE == 1 static void test_advanced_local_pubsub(void) { printf("test_advanced_local_pubsub\n"); const char *expr = "zenoh-pico/advanced-pubsub/test/local_pubsub"; z_owned_session_t s; z_owned_config_t c; z_config_default(&c); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ASSERT_OK(z_open(&s, z_config_move(&c), NULL)); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); pub_opts.cache.is_enabled = true; pub_opts.cache.max_samples = 1; pub_opts.sample_miss_detection.is_enabled = true; ASSERT_OK(ze_declare_advanced_publisher(z_loan(s), &pub, z_loan(k), &pub_opts)); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_history_options_default(&sub_opts.history); ze_advanced_subscriber_recovery_options_default(&sub_opts.recovery); ze_advanced_subscriber_last_sample_miss_detection_options_default(&sub_opts.recovery.last_sample_miss_detection); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 1)); ASSERT_OK(ze_declare_advanced_subscriber(z_loan(s), &sub, z_loan(k), z_move(closure), &sub_opts)); // z_sleep_ms(TEST_SLEEP_MS); put_str(z_loan(pub), "1"); z_sleep_ms(TEST_SLEEP_MS); expect_next(z_loan(handler), "1"); expect_empty(z_loan(handler)); ze_advanced_subscriber_drop(z_move(sub)); ze_advanced_publisher_drop(z_move(pub)); z_fifo_handler_sample_drop(z_move(handler)); z_session_drop(z_session_move(&s)); } #endif int main(int argc, char **argv) { (void)argc; (void)argv; test_advanced_history(false); #if defined(ZENOH_LINUX) test_advanced_history(true); #endif #ifdef Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY test_advanced_retransmission(); test_advanced_retransmission_periodic(); test_advanced_retransmission_heartbeat(); test_advanced_sample_miss(); test_advanced_retransmission_sample_miss(); // test_advanced_late_joiner(); #endif #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 && Z_FEATURE_LOCAL_QUERYABLE == 1 test_advanced_local_pubsub(); #endif return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf( "Missing config token to build this test. This test requires: Z_FEATURE_ADVANCED_PUBLICATION " "and Z_FEATURE_ADVANCED_SUBSCRIPTION\n"); return 0; } #endif ================================================ FILE: tests/z_api_alignment_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include "zenoh-pico/utils/result.h" #undef NDEBUG #include #define URI "demo/example/**/*" // Don't include due to clash with Zenoh Pico system includes #if !defined(WIN32) && !defined(_WIN32) && (!defined(__WIN32) || defined(__CYGWIN__)) #include #endif #include "zenoh-pico.h" #define SLEEP 2 #define SCOUTING_TIMEOUT "1000" #define assert_eq(x, y) \ { \ int l = (int)x; \ int r = (int)y; \ if (l != r) { \ printf("assert_eq failed: l=%d, r=%d\n", l, r); \ assert(false); \ } \ } const char *value = "Test value"; volatile unsigned int zids = 0; void zid_handler(const z_id_t *id, void *arg) { (void)(arg); (void)(id); printf("%s\n", __func__); zids++; } volatile unsigned int hellos = 0; void hello_handler(z_loaned_hello_t *hello, void *arg) { (void)hello; (void)(arg); printf("%s\n", __func__); hellos++; } volatile unsigned int queries = 0; void query_handler(z_loaned_query_t *query, void *arg) { printf("%s\n", __func__); queries++; const z_loaned_keyexpr_t *query_ke = z_query_keyexpr(query); z_view_string_t k_str; z_keyexpr_as_view_string(query_ke, &k_str); (void)arg; assert(!z_view_string_is_empty(&k_str)); z_view_string_t pred; z_query_parameters(query, &pred); (void)(pred); const z_loaned_bytes_t *payload = z_query_payload(query); (void)(payload); z_query_reply_options_t _ret_qreply_opt; z_query_reply_options_default(&_ret_qreply_opt); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, value); z_query_reply(query, query_ke, z_move(reply_payload), &_ret_qreply_opt); } volatile unsigned int replies = 0; void reply_handler(z_loaned_reply_t *reply, void *arg) { printf("%s\n", __func__); replies++; (void)arg; if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); z_view_string_t k_str; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k_str); (void)arg; assert(!z_view_string_is_empty(&k_str)); } else { const z_loaned_reply_err_t *_ret_zerr = z_reply_err(reply); (void)(_ret_zerr); } } volatile unsigned int datas = 0; void data_handler(z_loaned_sample_t *sample, void *arg) { printf("%s\n", __func__); datas++; z_view_string_t k_str; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k_str); (void)arg; assert(!z_view_string_is_empty(&k_str)); const char *encoding_expected = z_sample_kind(sample) == Z_SAMPLE_KIND_PUT ? "zenoh/bytes;test_encoding" : "zenoh/bytes"; z_owned_string_t encoding; z_encoding_to_string(z_sample_encoding(sample), &encoding); printf("%s\n", z_string_data(z_loan(encoding))); assert(strncmp(encoding_expected, z_string_data(z_loan(encoding)), strlen(encoding_expected)) == 0); z_drop(z_move(encoding)); } int main(int argc, char **argv) { assert_eq(argc, 2); (void)(argc); setvbuf(stdout, NULL, _IOLBF, 1024); #ifdef ZENOH_C zc_init_logger(); const char *encoding_expected = z_sample_kind(sample) == Z_SAMPLE_KIND_PUT ? "zenoh/bytes" : "zenoh/bytes;test_encoding"; #endif z_view_keyexpr_t key_demo_example, key_demo_example_a, key_demo_example_starstar; z_view_keyexpr_from_str(&key_demo_example, "demo/example"); z_view_keyexpr_from_str(&key_demo_example_a, "demo/example/a"); z_view_keyexpr_from_str(&key_demo_example_starstar, "demo/example/**"); bool _ret_bool = z_keyexpr_includes(z_loan(key_demo_example_starstar), z_loan(key_demo_example_a)); assert(_ret_bool); _ret_bool = z_keyexpr_intersects(z_loan(key_demo_example_starstar), z_loan(key_demo_example_a)); assert(_ret_bool); _ret_bool = z_keyexpr_equals(z_loan(key_demo_example_starstar), z_loan(key_demo_example)); assert(!_ret_bool); z_sleep_s(SLEEP); size_t keyexpr_len = strlen(URI); char *keyexpr_str = (char *)z_malloc(keyexpr_len + 1); memcpy(keyexpr_str, URI, keyexpr_len); keyexpr_str[keyexpr_len] = '\0'; z_result_t _ret_res = z_keyexpr_is_canon(keyexpr_str, keyexpr_len); assert(_ret_res < 0); _ret_res = z_keyexpr_canonize(keyexpr_str, &keyexpr_len); assert_eq(_ret_res, 0); assert_eq(strlen(URI), keyexpr_len); printf("Ok\n"); z_sleep_s(SLEEP); printf("Testing Configs..."); z_owned_config_t _ret_config; z_config_default(&_ret_config); assert(z_internal_check(_ret_config)); #ifdef ZENOH_PICO _ret_res = zp_config_insert(z_loan_mut(_ret_config), Z_CONFIG_CONNECT_KEY, argv[1]); assert_eq(_ret_res, 0); const char *_ret_cstr = zp_config_get(z_loan(_ret_config), Z_CONFIG_CONNECT_KEY); assert_eq(strlen(_ret_cstr), strlen(argv[1])); assert_eq(strncmp(_ret_cstr, argv[1], strlen(_ret_cstr)), 0); #endif z_owned_config_t _ret_sconfig; z_config_default(&_ret_sconfig); assert(z_internal_check(_ret_sconfig)); printf("Ok\n"); z_sleep_s(SLEEP); printf("Testing Scouting..."); z_owned_closure_hello_t _ret_closure_hello; z_closure(&_ret_closure_hello, hello_handler, NULL, NULL); _ret_res = z_scout(z_move(_ret_sconfig), z_move(_ret_closure_hello), NULL); assert_eq(_ret_res, 0); assert(hellos >= 1); uint32_t _scouting_timeout = (uint32_t)strtoul(SCOUTING_TIMEOUT, NULL, 10); z_sleep_ms(_scouting_timeout); printf("Ok\n"); z_sleep_s(SLEEP); z_owned_session_t s1; z_open(&s1, z_move(_ret_config), NULL); assert(z_internal_check(s1)); z_id_t _ret_zid = z_info_zid(z_loan(s1)); printf("Session 1 with PID: 0x"); z_owned_string_t id_str; z_id_to_string(&_ret_zid, &id_str); printf("%.*s\n", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str))); z_drop(z_move(id_str)); z_owned_closure_zid_t _ret_closure_zid; z_closure(&_ret_closure_zid, zid_handler, NULL, NULL); _ret_res = z_info_peers_zid(z_loan(s1), z_move(_ret_closure_zid)); assert_eq(_ret_res, 0); z_sleep_s(SLEEP); assert_eq(zids, 0); z_closure(&_ret_closure_zid, zid_handler, NULL, NULL); _ret_res = z_info_routers_zid(z_loan(s1), z_move(_ret_closure_zid)); assert_eq(_ret_res, 0); z_sleep_s(SLEEP); assert_eq(zids, 1); z_sleep_s(SLEEP); z_config_default(&_ret_config); #ifdef ZENOH_PICO _ret_res = zp_config_insert(z_loan_mut(_ret_config), Z_CONFIG_CONNECT_KEY, argv[1]); assert_eq(_ret_res, 0); _ret_cstr = zp_config_get(z_loan(_ret_config), Z_CONFIG_CONNECT_KEY); assert_eq(strlen(_ret_cstr), strlen(argv[1])); assert_eq(strncmp(_ret_cstr, argv[1], strlen(_ret_cstr)), 0); #endif z_owned_session_t s2; z_open(&s2, z_move(_ret_config), NULL); assert(z_internal_check(s2)); _ret_zid = z_info_zid(z_loan(s2)); printf("Session 2 with PID: 0x"); z_id_to_string(&_ret_zid, &id_str); printf("%.*s\n", (int)z_string_len(z_loan(id_str)), z_string_data(z_loan(id_str))); z_drop(z_move(id_str)); z_sleep_s(SLEEP); const z_loaned_session_t *ls1 = z_loan(s1); printf("Declaring Subscriber..."); z_owned_closure_sample_t _ret_closure_sample; z_closure(&_ret_closure_sample, data_handler, NULL, &ls1); z_subscriber_options_t _ret_sub_opt; z_subscriber_options_default(&_ret_sub_opt); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, keyexpr_str); z_owned_subscriber_t _ret_sub; _ret_res = z_declare_subscriber(z_loan(s2), &_ret_sub, z_loan(ke), z_move(_ret_closure_sample), &_ret_sub_opt); assert(_ret_res == _Z_RES_OK); printf("Ok\n"); z_sleep_s(SLEEP); char s1_res[64]; sprintf(s1_res, "%s/chunk/%d", keyexpr_str, 1); z_view_keyexpr_t s1_key; z_view_keyexpr_from_str(&s1_key, s1_res); z_owned_keyexpr_t _ret_expr; z_declare_keyexpr(z_loan(s1), &_ret_expr, z_loan(s1_key)); assert(z_internal_check(_ret_expr)); printf("Ok\n"); printf("Session Put..."); z_put_options_t _ret_put_opt; z_put_options_default(&_ret_put_opt); z_owned_encoding_t encoding; z_encoding_from_str(&encoding, "test_encoding"); _ret_put_opt.encoding = z_move(encoding); _ret_put_opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; // Create payload z_owned_bytes_t payload; z_bytes_from_str(&payload, (char *)value, NULL, NULL); _ret_res = z_put(z_loan(s1), z_loan(_ret_expr), z_move(payload), &_ret_put_opt); assert_eq(_ret_res, 0); assert(!z_internal_check(encoding)); printf("Ok\n"); z_sleep_s(SLEEP); assert_eq(datas, 1); printf("Session delete..."); z_delete_options_t _ret_delete_opt; z_delete_options_default(&_ret_delete_opt); _ret_delete_opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; _ret_res = z_delete(z_loan(s1), z_loan(_ret_expr), &_ret_delete_opt); assert_eq(_ret_res, 0); printf("Ok\n"); z_sleep_s(SLEEP); assert_eq(datas, 2); printf("Declaring Publisher..."); z_publisher_options_t _ret_pub_opt; z_publisher_options_default(&_ret_pub_opt); z_encoding_from_str(&encoding, "test_encoding"); _ret_pub_opt.encoding = z_move(encoding); _ret_pub_opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; z_owned_publisher_t _ret_pub; _ret_res = z_declare_publisher(z_loan(s1), &_ret_pub, z_loan(s1_key), &_ret_pub_opt); assert(_ret_res == _Z_RES_OK); assert(!z_internal_check(encoding)); printf("Ok\n"); z_sleep_s(SLEEP); printf("Publisher Put..."); // Create payload z_bytes_copy_from_str(&payload, value); z_publisher_put_options_t _ret_pput_opt; z_publisher_put_options_default(&_ret_pput_opt); _ret_res = z_publisher_put(z_loan(_ret_pub), z_move(payload), &_ret_pput_opt); assert_eq(_ret_res, 0); printf("Ok\n"); z_sleep_s(SLEEP); assert_eq(datas, 3); printf("Publisher Delete..."); z_publisher_delete_options_t _ret_pdelete_opt; z_publisher_delete_options_default(&_ret_pdelete_opt); _ret_res = z_publisher_delete(z_loan(_ret_pub), &_ret_pdelete_opt); assert_eq(_ret_res, 0); printf("Ok\n"); z_sleep_s(SLEEP); assert_eq(datas, 4); printf("Undeclaring Publisher..."); z_drop(z_move(_ret_pub)); assert(!z_internal_check(_ret_pub)); printf("Ok\n"); z_sleep_s(SLEEP); printf("Undeclaring Subscriber..."); z_drop(z_move(_ret_sub)); assert(!z_internal_check(_ret_sub)); printf("Ok\n"); z_sleep_s(SLEEP); printf("Declaring Queryable..."); z_owned_closure_query_t _ret_closure_query; z_closure(&_ret_closure_query, query_handler, NULL, &ls1); z_queryable_options_t _ret_qle_opt; z_queryable_options_default(&_ret_qle_opt); z_owned_queryable_t qle; assert(z_declare_queryable(z_loan(s1), &qle, z_loan(s1_key), z_move(_ret_closure_query), &_ret_qle_opt) == _Z_RES_OK); printf("Ok\n"); z_sleep_s(SLEEP); printf("Testing Consolidations..."); const z_loaned_session_t *ls2 = z_loan(s2); z_owned_closure_reply_t _ret_closure_reply; z_closure(&_ret_closure_reply, reply_handler, NULL, &ls2); z_get_options_t _ret_get_opt; z_get_options_default(&_ret_get_opt); _ret_get_opt.target = z_query_target_default(); _ret_get_opt.consolidation = z_query_consolidation_auto(); (void)(_ret_get_opt.consolidation); _ret_get_opt.consolidation = z_query_consolidation_default(); (void)(_ret_get_opt.consolidation); _ret_get_opt.consolidation = z_query_consolidation_latest(); (void)(_ret_get_opt.consolidation); _ret_get_opt.consolidation = z_query_consolidation_monotonic(); (void)(_ret_get_opt.consolidation); _ret_get_opt.consolidation = z_query_consolidation_none(); (void)(_ret_get_opt.consolidation); printf("Ok\n"); printf("Testing Get..."); _ret_res = z_get(z_loan(s2), z_loan(s1_key), "", z_move(_ret_closure_reply), &_ret_get_opt); assert_eq(_ret_res, 0); printf("Ok\n"); z_sleep_s(SLEEP); assert_eq(queries, 1); assert_eq(replies, 1); printf("Undeclaring Queryable..."); z_drop(z_move(qle)); printf("Ok\n"); printf("Undeclaring Keyexpr..."); _ret_res = z_undeclare_keyexpr(z_loan(s1), z_move(_ret_expr)); printf(" %02x\n", _ret_res); assert_eq(_ret_res, 0); assert(!z_internal_check(_ret_expr)); printf("Ok\n"); printf("Close sessions..."); _ret_res = z_close(z_loan_mut(s1), NULL); assert_eq(_ret_res, 0); z_drop(z_move(s1)); _ret_res = z_close(z_loan_mut(s2), NULL); assert_eq(_ret_res, 0); z_drop(z_move(s2)); printf("Ok\n"); z_sleep_s(SLEEP * 5); z_free(keyexpr_str); return 0; } ================================================ FILE: tests/z_api_batching_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #if Z_FEATURE_BATCHING == 1 void test_batching_while_connected(void) { printf("test_batching_while_connected\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); ASSERT_OK(z_open(&s1, z_move(c1), NULL)); ASSERT_OK(z_open(&s2, z_move(c2), NULL)); z_sleep_ms(1000); // Wait for connection to establish ASSERT_OK(zp_batch_start(z_loan_mut(s2))); ASSERT_OK(zp_batch_flush(z_loan_mut(s2))); ASSERT_OK(zp_batch_stop(z_loan_mut(s2))); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } void test_batching_after_disconnection(void) { printf("test_batching_after_disconnection\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:12345"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "client"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:12345"); ASSERT_OK(z_open(&s1, z_move(c1), NULL)); ASSERT_OK(z_open(&s2, z_move(c2), NULL)); // Wait for connection to establish z_sleep_ms(1000); // Drop session and wait for it to be detected as dropped z_session_drop(z_session_move(&s1)); z_sleep_ms(10000); ASSERT_ERR(zp_batch_start(z_loan_mut(s2)), _Z_ERR_TRANSPORT_NOT_AVAILABLE); ASSERT_ERR(zp_batch_flush(z_loan_mut(s2)), _Z_ERR_TRANSPORT_NOT_AVAILABLE); ASSERT_ERR(zp_batch_stop(z_loan_mut(s2)), _Z_ERR_TRANSPORT_NOT_AVAILABLE); z_session_drop(z_session_move(&s2)); } int main(int argc, char **argv) { (void)argc; (void)argv; test_batching_while_connected(); test_batching_after_disconnection(); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf("Missing config token to build this test. This test requires: Z_FEATURE_BATCHING\n"); return 0; } #endif // Z_FEATURE_BATCHING == 1 ================================================ FILE: tests/z_api_bytes_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/serialization.h" #include "zenoh-pico/api/types.h" #undef NDEBUG #include void test_reader_seek(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, 10, NULL, NULL); z_bytes_reader_t reader = z_bytes_get_reader(z_bytes_loan(&payload)); assert(z_bytes_reader_tell(&reader) == 0); assert(0 == z_bytes_reader_seek(&reader, 5, SEEK_CUR)); assert(z_bytes_reader_tell(&reader) == 5); assert(0 == z_bytes_reader_seek(&reader, 7, SEEK_SET)); assert(z_bytes_reader_tell(&reader) == 7); assert(0 == z_bytes_reader_seek(&reader, -1, SEEK_END)); assert(z_bytes_reader_tell(&reader) == 9); assert(z_bytes_reader_seek(&reader, 20, SEEK_SET) < 0); assert(0 == z_bytes_reader_seek(&reader, 5, SEEK_SET)); assert(z_bytes_reader_tell(&reader) == 5); assert(z_bytes_reader_seek(&reader, 10, SEEK_CUR) < 0); assert(z_bytes_reader_seek(&reader, 10, SEEK_END) < 0); assert(z_bytes_reader_seek(&reader, -20, SEEK_END) < 0); z_bytes_drop(z_bytes_move(&payload)); } void test_reader_read(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; uint8_t data_out[10] = {0}; z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, 10, NULL, NULL); z_bytes_reader_t reader = z_bytes_get_reader(z_bytes_loan(&payload)); assert(5 == z_bytes_reader_read(&reader, data_out, 5)); assert(5 == z_bytes_reader_remaining(&reader)); z_bytes_reader_seek(&reader, 2, SEEK_CUR); assert(2 == z_bytes_reader_read(&reader, data_out + 7, 2)); assert(1 == z_bytes_reader_remaining(&reader)); z_bytes_reader_seek(&reader, 5, SEEK_SET); assert(2 == z_bytes_reader_read(&reader, data_out + 5, 2)); assert(3 == z_bytes_reader_remaining(&reader)); z_bytes_reader_seek(&reader, -1, SEEK_END); assert(1 == z_bytes_reader_read(&reader, data_out + 9, 10)); assert(0 == z_bytes_reader_remaining(&reader)); assert(0 == z_bytes_reader_read(&reader, data_out, 10)); assert(!memcmp(data, data_out, 10)); z_bytes_drop(z_bytes_move(&payload)); } void test_writer(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; uint8_t data_out[10] = {0}; z_owned_bytes_writer_t writer; z_bytes_writer_empty(&writer); assert(z_bytes_writer_write_all(z_bytes_writer_loan_mut(&writer), data, 3) == 0); assert(z_bytes_writer_write_all(z_bytes_writer_loan_mut(&writer), data + 3, 5) == 0); assert(z_bytes_writer_write_all(z_bytes_writer_loan_mut(&writer), data + 8, 2) == 0); z_owned_bytes_t payload; z_bytes_writer_finish(z_bytes_writer_move(&writer), &payload); z_bytes_reader_t reader = z_bytes_get_reader(z_bytes_loan(&payload)); assert(10 == z_bytes_reader_read(&reader, data_out, 10)); assert(0 == memcmp(data, data_out, 10)); z_bytes_drop(z_bytes_move(&payload)); } void test_append(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; uint8_t data_out[10] = {0}; z_owned_bytes_t payload; z_bytes_copy_from_buf(&payload, data, 5); z_owned_bytes_writer_t writer; z_bytes_writer_empty(&writer); z_bytes_writer_append(z_bytes_writer_loan_mut(&writer), z_bytes_move(&payload)); { z_owned_bytes_t b; z_bytes_copy_from_buf(&b, data + 5, 5); assert(z_bytes_writer_append(z_bytes_writer_loan_mut(&writer), z_bytes_move(&b)) == 0); } z_bytes_writer_finish(z_bytes_writer_move(&writer), &payload); z_bytes_reader_t reader = z_bytes_get_reader(z_bytes_loan(&payload)); z_bytes_reader_read(&reader, data_out, 10); assert(!memcmp(data, data_out, 10)); uint8_t d; assert(0 == z_bytes_reader_read(&reader, &d, 1)); // we reached the end of the payload z_bytes_drop(z_bytes_move(&payload)); } void custom_deleter(void *data, void *context) { (void)data; size_t *cnt = (size_t *)context; (*cnt)++; } bool z_check_and_drop_payload(z_owned_bytes_t *payload, uint8_t *data, size_t len) { z_owned_slice_t out; z_bytes_to_slice(z_bytes_loan(payload), &out); z_bytes_drop(z_bytes_move(payload)); bool res = memcmp(data, z_slice_data(z_slice_loan(&out)), len) == 0; z_slice_drop(z_slice_move(&out)); return res; } void test_slice(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; size_t cnt = 0; z_owned_bytes_t payload; z_bytes_from_buf(&payload, data, 10, custom_deleter, (void *)&cnt); z_owned_slice_t out; z_bytes_to_slice(z_bytes_loan(&payload), &out); assert(cnt == 0); z_bytes_drop(z_bytes_move(&payload)); assert(cnt == 1); assert(!memcmp(data, z_slice_data(z_slice_loan(&out)), 10)); z_slice_drop(z_slice_move(&out)); z_owned_bytes_t payload2; z_owned_slice_t s; z_slice_copy_from_buf(&s, data, 10); z_bytes_copy_from_slice(&payload2, z_slice_loan(&s)); assert(z_internal_slice_check(&s)); z_slice_drop(z_slice_move(&s)); assert(z_check_and_drop_payload(&payload2, data, 10)); z_owned_bytes_t payload3; z_slice_copy_from_buf(&s, data, 10); z_bytes_from_slice(&payload3, z_slice_move(&s)); assert(!z_internal_slice_check(&s)); assert(z_check_and_drop_payload(&payload3, data, 10)); z_owned_bytes_t payload4; z_bytes_copy_from_buf(&payload4, data, 10); assert(z_check_and_drop_payload(&payload4, data, 10)); z_owned_bytes_t payload5; z_bytes_from_static_buf(&payload5, data, 10); assert(z_check_and_drop_payload(&payload5, data, 10)); } #define TEST_ARITHMETIC(TYPE, EXT, VAL) \ { \ TYPE in = VAL, out; \ z_owned_bytes_t payload; \ ze_serialize_##EXT(&payload, in); \ ze_deserialize_##EXT(z_bytes_loan(&payload), &out); \ assert(in == out); \ z_bytes_drop(z_bytes_move(&payload)); \ } void test_arithmetic(void) { TEST_ARITHMETIC(uint8_t, uint8, 5); TEST_ARITHMETIC(uint16_t, uint16, 1000); TEST_ARITHMETIC(uint32_t, uint32, 51000000); TEST_ARITHMETIC(uint64_t, uint64, 1000000000005); TEST_ARITHMETIC(int8_t, int8, 5); TEST_ARITHMETIC(int16_t, int16, -1000); TEST_ARITHMETIC(int32_t, int32, 51000000); TEST_ARITHMETIC(int64_t, int64, -1000000000005); TEST_ARITHMETIC(float, float, 10.1f); TEST_ARITHMETIC(double, double, -105.001); } bool check_slice(const z_loaned_bytes_t *b, const uint8_t *data, size_t len) { z_bytes_slice_iterator_t it = z_bytes_get_slice_iterator(b); uint8_t *data_out = (uint8_t *)malloc(len); z_view_slice_t v; size_t pos = 0; while (z_bytes_slice_iterator_next(&it, &v)) { const uint8_t *slice_data = z_slice_data(z_view_slice_loan(&v)); size_t slice_len = z_slice_len(z_view_slice_loan(&v)); memcpy(data_out + pos, slice_data, slice_len); pos += slice_len; } assert(pos == len); assert(memcmp(data, data_out, len) == 0); free(data_out); return true; } void test_slices(void) { uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; z_owned_bytes_t payload; z_bytes_copy_from_buf(&payload, data, 10); assert(check_slice(z_bytes_loan(&payload), data, 10)); #if defined(Z_FEATURE_UNSTABLE_API) z_view_slice_t view; assert(z_bytes_get_contiguous_view(z_bytes_loan(&payload), &view) == Z_OK); assert(z_slice_len(z_view_slice_loan(&view)) == 10); assert(memcmp(data, z_slice_data(z_view_slice_loan(&view)), 10) == 0); #endif z_bytes_drop(z_bytes_move(&payload)); z_owned_bytes_writer_t writer; z_bytes_writer_empty(&writer); // possible multiple slices // reusing empty writer for (size_t i = 0; i < 10; i++) { z_owned_bytes_t b; z_bytes_copy_from_buf(&b, data + i, 1); z_bytes_writer_append(z_bytes_writer_loan_mut(&writer), z_bytes_move(&b)); } z_bytes_writer_finish(z_bytes_writer_move(&writer), &payload); assert(check_slice(z_bytes_loan(&payload), data, 10)); #if defined(Z_FEATURE_UNSTABLE_API) assert(z_bytes_get_contiguous_view(z_bytes_loan(&payload), &view) != Z_OK); #endif z_bytes_drop(z_bytes_move(&payload)); } void test_serialize_simple(void) { z_owned_bytes_t b; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); assert(ze_serializer_serialize_bool(ze_serializer_loan_mut(&serializer), true) == 0); assert(ze_serializer_serialize_bool(ze_serializer_loan_mut(&serializer), false) == 0); assert(ze_serializer_serialize_double(ze_serializer_loan_mut(&serializer), 0.5) == 0); assert(ze_serializer_serialize_int32(ze_serializer_loan_mut(&serializer), -1111) == 0); assert(ze_serializer_serialize_str(ze_serializer_loan_mut(&serializer), "abc") == 0); ze_serializer_finish(ze_serializer_move(&serializer), &b); double d; int32_t i; z_owned_string_t s; bool bl = false; ze_deserializer_t deserializer = ze_deserializer_from_bytes(z_bytes_loan(&b)); assert(!ze_deserializer_is_done(&deserializer)); assert(ze_deserializer_deserialize_bool(&deserializer, &bl) == 0); assert(bl); assert(ze_deserializer_deserialize_bool(&deserializer, &bl) == 0); assert(!bl); assert(ze_deserializer_deserialize_double(&deserializer, &d) == 0); assert(ze_deserializer_deserialize_int32(&deserializer, &i) == 0); assert(ze_deserializer_deserialize_string(&deserializer, &s) == 0); assert(ze_deserializer_is_done(&deserializer)); assert(d == 0.5); assert(i == -1111); assert(strncmp("abc", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_string_drop(z_string_move(&s)); z_bytes_drop(z_bytes_move(&b)); } void test_serialize_sequence(void) { uint32_t input[6] = {1, 2, 3, 100, 10000, 100000}; z_owned_bytes_t b; ze_owned_serializer_t serializer; ze_serializer_empty(&serializer); ze_serializer_serialize_sequence_length(ze_serializer_loan_mut(&serializer), 6); for (size_t i = 0; i < 6; ++i) { ze_serializer_serialize_uint32(ze_serializer_loan_mut(&serializer), input[i]); } ze_serializer_finish(ze_serializer_move(&serializer), &b); ze_deserializer_t deserializer = ze_deserializer_from_bytes(z_bytes_loan(&b)); size_t len = 0; assert(!ze_deserializer_is_done(&deserializer)); assert(ze_deserializer_deserialize_sequence_length(&deserializer, &len) == 0); assert(len == 6); for (size_t i = 0; i < 6; i++) { uint32_t u = 0; assert(ze_deserializer_deserialize_uint32(&deserializer, &u) == 0); assert(u == input[i]); } assert(ze_deserializer_is_done(&deserializer)); z_bytes_drop(z_bytes_move(&b)); } int main(void) { test_reader_seek(); test_reader_read(); test_writer(); test_slice(); test_append(); test_slices(); test_arithmetic(); test_serialize_simple(); test_serialize_sequence(); } ================================================ FILE: tests/z_api_callback_drop_on_undeclare_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #ifdef Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY #include "utils/tcp_proxy.h" #endif #undef NDEBUG #include typedef struct callback_arg_t { volatile bool called; volatile bool dropped; } callback_arg_t; #if Z_FEATURE_MULTI_THREAD == 1 typedef struct thread_arg_t { z_owned_session_t* session; z_view_keyexpr_t* ke; } thread_arg_t; #endif z_result_t open_session(z_owned_session_t* s) { z_owned_config_t config; _Z_RETURN_IF_ERR(z_config_default(&config)); return z_open(s, z_config_move(&config), NULL); } #define DECLARE_ON_RECEIVE_HANDLER(_type, cv) \ void on_receive_##_type##_handler(cv _type* msg, void* arg) { \ (void)msg; \ callback_arg_t* callback_arg = (callback_arg_t*)arg; \ z_sleep_s(3); \ callback_arg->called = true; \ } \ void on_drop_##_type##_handler(void* arg) { \ callback_arg_t* callback_arg = (callback_arg_t*)arg; \ z_sleep_s(1); \ callback_arg->dropped = true; \ } #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 #if Z_FEATURE_MULTI_THREAD == 1 void* put_from_another_thread(void* targ) { thread_arg_t* arg = (thread_arg_t*)targ; z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); assert(z_put(z_session_loan(arg->session), z_view_keyexpr_loan(arg->ke), z_bytes_move(&payload), NULL) == Z_OK); return NULL; } #endif DECLARE_ON_RECEIVE_HANDLER(z_loaned_sample_t, _ZP_NOTHING) void test_subscriber_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_owned_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/subscriber_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/subscriber_callback_drop") == Z_OK); } callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Subscriber callback drop on undeclare: background = %d\n", background); z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); if (!background) { assert(z_declare_subscriber(z_session_loan(&session1), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } else { assert(z_declare_background_subscriber(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } z_sleep_s(2); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); assert(z_put(z_session_loan(&session2), z_view_keyexpr_loan(&ke), z_bytes_move(&payload), NULL) == Z_OK); z_sleep_s(1); if (!background) { z_undeclare_subscriber(z_subscriber_move(&subscriber)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.called == true); assert(arg.dropped == true); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 && Z_FEATURE_MULTI_THREAD == 1 printf("Test: Local Subscriber callback drop on undeclare: background = %d\n", background); arg.called = false; arg.dropped = false; z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); if (!background) { assert(z_declare_subscriber(z_session_loan(&session2), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } else { assert(z_declare_background_subscriber(z_session_loan(&session2), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } thread_arg_t thread_arg = { .session = &session2, .ke = &ke, }; z_owned_task_t task; z_task_init(&task, NULL, put_from_another_thread, &thread_arg); z_sleep_s(1); if (!background) { z_undeclare_subscriber(z_subscriber_move(&subscriber)); } else { z_close(z_session_loan_mut(&session2), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_task_join(z_task_move(&task)); #endif z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } #if Z_FEATURE_LIVELINESS == 1 void test_liveliness_subscriber_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_owned_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/liveliness_subscriber_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/liveliness_subscriber_callback_drop") == Z_OK); } callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Liveliness Subscriber callback drop on undeclare: background = %d\n", background); z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); if (!background) { assert(z_liveliness_declare_subscriber(z_session_loan(&session1), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } else { assert(z_liveliness_declare_background_subscriber(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } z_sleep_s(2); z_owned_liveliness_token_t token; assert(z_liveliness_declare_token(z_session_loan(&session2), &token, z_view_keyexpr_loan(&ke), NULL) == Z_OK); z_sleep_s(1); if (!background) { z_undeclare_subscriber(z_subscriber_move(&subscriber)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_liveliness_token_drop(z_liveliness_token_move(&token)); z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } #endif #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 && Z_FEATURE_ADVANCED_PUBLICATION == 1 void test_advanced_subscriber_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; ze_owned_advanced_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/advanced_subscriber_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/advanced_subscriber_callback_drop") == Z_OK); } callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Advanced Subscriber callback drop on undeclare: background = %d\n", background); z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); if (!background) { assert(ze_declare_advanced_subscriber(z_session_loan(&session1), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } else { assert(ze_declare_background_advanced_subscriber(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } z_sleep_s(2); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); assert(z_put(z_session_loan(&session2), z_view_keyexpr_loan(&ke), z_bytes_move(&payload), NULL) == Z_OK); z_sleep_s(1); if (!background) { ze_undeclare_advanced_subscriber(ze_advanced_subscriber_move(&subscriber)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.dropped == true); assert(arg.called == true); z_session_drop(z_session_move(&session1)); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 && Z_FEATURE_MULTI_THREAD == 1 printf("Test: Local Advanced Subscriber callback drop on undeclare: background = %d\n", background); arg.called = false; arg.dropped = false; z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); if (!background) { assert(ze_declare_advanced_subscriber(z_session_loan(&session2), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } else { assert(ze_declare_background_advanced_subscriber(z_session_loan(&session2), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == Z_OK); } thread_arg_t thread_arg = { .session = &session2, .ke = &ke, }; z_owned_task_t task; z_task_init(&task, NULL, put_from_another_thread, &thread_arg); z_sleep_s(1); if (!background) { ze_undeclare_advanced_subscriber(ze_advanced_subscriber_move(&subscriber)); } else { z_close(z_session_loan_mut(&session2), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_task_join(z_task_move(&task)); #endif z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } void test_advanced_subscriber_late_join_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/late_advanced_subscriber_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/late_advanced_subscriber_callback_drop") == Z_OK); } printf("Test: Advanced Subscriber late join callback drop on undeclare: background = %d\n", background); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_cache_options_default(&pub_opts.cache); pub_opts.cache.max_samples = 10; pub_opts.publisher_detection = true; assert(ze_declare_advanced_publisher(z_loan(session2), &pub, z_view_keyexpr_loan(&ke), &pub_opts) == Z_OK); z_sleep_s(1); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); ze_advanced_publisher_put(z_loan(pub), z_bytes_move(&payload), NULL); callback_arg_t arg; arg.called = false; arg.dropped = false; z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); ze_advanced_subscriber_history_options_default(&sub_opts.history); sub_opts.history.detect_late_publishers = true; if (!background) { assert(ze_declare_advanced_subscriber(z_session_loan(&session1), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), &sub_opts) == Z_OK); } else { assert(ze_declare_background_advanced_subscriber(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), &sub_opts) == Z_OK); } z_sleep_s(1); if (!background) { ze_undeclare_advanced_subscriber(ze_advanced_subscriber_move(&sub)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.called == true); assert(arg.dropped == true); ze_advanced_publisher_drop(ze_advanced_publisher_move(&pub)); z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } #if Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY == 1 DECLARE_ON_RECEIVE_HANDLER(ze_miss_t, const) void put_str(const ze_loaned_advanced_publisher_t* pub, const char* str) { z_owned_bytes_t payload; z_bytes_from_static_str(&payload, str); ze_advanced_publisher_put(pub, z_bytes_move(&payload), NULL); } void setup_two_peers_with_proxy(z_owned_session_t* s1, z_owned_session_t* s2, tcp_proxy_t** proxy, uint16_t upstream_listen_port) { *proxy = tcp_proxy_create("127.0.0.1", "127.0.0.1", upstream_listen_port); assert(*proxy != NULL); int port = tcp_proxy_start(*proxy, 100); assert(port > 0); z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); char uri[128]; // s1 listens on the fixed upstream port zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); snprintf(uri, sizeof(uri), "tcp/127.0.0.1:%u#iface=lo", (unsigned)upstream_listen_port); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, uri); // s2 connects via proxy ephemeral port zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); snprintf(uri, sizeof(uri), "tcp/127.0.0.1:%u#iface=lo", (unsigned)port); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, uri); assert(z_open(s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(s2, z_config_move(&c2), NULL) == Z_OK); } void test_advanced_sample_miss_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_view_keyexpr_t ke; if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/sample_miss_listener_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/sample_miss_listener_callback_drop") == Z_OK); } printf("Test: Sample Miss Listener callback drop on undeclare: background = %d\n", background); tcp_proxy_t* tcp_proxy; setup_two_peers_with_proxy(&session1, &session2, &tcp_proxy, 9000); ze_owned_advanced_subscriber_t sub; ze_advanced_subscriber_options_t sub_opts; ze_advanced_subscriber_options_default(&sub_opts); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; assert(z_fifo_channel_sample_new(&closure, &handler, 10) == Z_OK); assert(ze_declare_advanced_subscriber(z_session_loan(&session2), &sub, z_loan(ke), z_move(closure), &sub_opts) == Z_OK); ze_owned_sample_miss_listener_t miss_listener; ze_owned_closure_miss_t miss_closure; callback_arg_t arg; arg.called = false; arg.dropped = false; ze_closure_miss(&miss_closure, on_receive_ze_miss_t_handler, on_drop_ze_miss_t_handler, (void*)&arg); if (!background) { assert(ze_advanced_subscriber_declare_sample_miss_listener(ze_advanced_subscriber_loan(&sub), &miss_listener, ze_closure_miss_move(&miss_closure)) == Z_OK); } else { assert(ze_advanced_subscriber_declare_background_sample_miss_listener( ze_advanced_subscriber_loan(&sub), ze_closure_miss_move(&miss_closure)) == Z_OK); } z_sleep_s(1); ze_owned_advanced_publisher_t pub; ze_advanced_publisher_options_t pub_opts; ze_advanced_publisher_options_default(&pub_opts); ze_advanced_publisher_sample_miss_detection_options_default(&pub_opts.sample_miss_detection); assert(ze_declare_advanced_publisher(z_session_loan(&session1), &pub, z_loan(ke), &pub_opts) == Z_OK); put_str(z_loan(pub), "1"); z_sleep_ms(500); put_str(z_loan(pub), "2"); z_sleep_ms(500); tcp_proxy_set_enabled(tcp_proxy, false); z_sleep_ms(500); put_str(z_loan(pub), "3"); z_sleep_ms(500); tcp_proxy_set_enabled(tcp_proxy, true); z_sleep_s(1); put_str(z_loan(pub), "4"); z_sleep_ms(500); if (!background) { ze_undeclare_sample_miss_listener(ze_sample_miss_listener_move(&miss_listener)); } else { ze_undeclare_advanced_subscriber(ze_advanced_subscriber_move(&sub)); } assert(arg.dropped == true); assert(arg.called == true); z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&handler)); if (!background) { ze_advanced_subscriber_drop(ze_advanced_subscriber_move(&sub)); } ze_advanced_publisher_drop(ze_advanced_publisher_move(&pub)); z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); tcp_proxy_stop(tcp_proxy); tcp_proxy_destroy(tcp_proxy); } #endif #endif #if Z_FEATURE_MATCHING == 1 #if Z_FEATURE_MULTI_THREAD == 1 typedef struct sub_thread_arg_t { z_owned_session_t* session; z_view_keyexpr_t* ke; z_owned_subscriber_t* sub; z_owned_closure_sample_t* closure; z_owned_fifo_handler_sample_t* handler; } sub_thread_arg_t; void* declare_sub_from_another_thread(void* targ) { sub_thread_arg_t* arg = (sub_thread_arg_t*)targ; z_owned_closure_sample_t sample_closure; z_owned_fifo_handler_sample_t sample_handler; assert(z_fifo_channel_sample_new(&sample_closure, &sample_handler, 3) == Z_OK); assert(z_declare_subscriber(z_session_loan(arg->session), arg->sub, z_view_keyexpr_loan(arg->ke), z_closure_sample_move(&sample_closure), NULL) == Z_OK); *(arg->handler) = sample_handler; return NULL; } #endif DECLARE_ON_RECEIVE_HANDLER(z_matching_status_t, const) void test_matching_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_view_keyexpr_t ke; z_owned_publisher_t pub; z_owned_subscriber_t sub; z_owned_matching_listener_t listener; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); printf("Test: Matching callback drop on undeclare: background = %d\n", background); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/matching_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/matching_callback_drop") == Z_OK); } assert(z_declare_publisher(z_session_loan(&session1), &pub, z_view_keyexpr_loan(&ke), NULL) == Z_OK); callback_arg_t arg; arg.called = false; arg.dropped = false; z_owned_closure_matching_status_t closure; z_closure_matching_status(&closure, on_receive_z_matching_status_t_handler, on_drop_z_matching_status_t_handler, (void*)&arg); if (!background) { assert(z_publisher_declare_matching_listener(z_publisher_loan(&pub), &listener, z_closure_matching_status_move(&closure)) == Z_OK); } else { assert(z_publisher_declare_background_matching_listener(z_publisher_loan(&pub), z_closure_matching_status_move(&closure)) == Z_OK); } z_sleep_s(1); z_owned_closure_sample_t sample_closure; z_owned_fifo_handler_sample_t sample_handler; assert(z_fifo_channel_sample_new(&sample_closure, &sample_handler, 3) == Z_OK); assert(z_declare_subscriber(z_session_loan(&session2), &sub, z_view_keyexpr_loan(&ke), z_closure_sample_move(&sample_closure), NULL) == Z_OK); z_sleep_s(1); if (!background) { z_matching_listener_drop(z_matching_listener_move(&listener)); } else { z_publisher_drop(z_publisher_move(&pub)); } assert(arg.dropped == true); assert(arg.called == true); z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&session1)); z_subscriber_drop(z_subscriber_move(&sub)); z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&sample_handler)); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 && Z_FEATURE_MULTI_THREAD == 1 printf("Test: Local Matching callback drop on undeclare: background = %d\n", background); assert(z_declare_publisher(z_session_loan(&session2), &pub, z_view_keyexpr_loan(&ke), NULL) == Z_OK); arg.called = false; arg.dropped = false; z_closure_matching_status(&closure, on_receive_z_matching_status_t_handler, on_drop_z_matching_status_t_handler, (void*)&arg); if (!background) { assert(z_publisher_declare_matching_listener(z_publisher_loan(&pub), &listener, z_closure_matching_status_move(&closure)) == Z_OK); } else { assert(z_publisher_declare_background_matching_listener(z_publisher_loan(&pub), z_closure_matching_status_move(&closure)) == Z_OK); } // Declare subscriber from another thread to avoid blocking on the matching callback sub_thread_arg_t sub_thread_arg = { .session = &session2, .ke = &ke, .sub = &sub, .closure = NULL, .handler = &sample_handler, }; z_owned_task_t task; z_task_init(&task, NULL, declare_sub_from_another_thread, &sub_thread_arg); z_sleep_s(1); if (!background) { z_matching_listener_drop(z_matching_listener_move(&listener)); } else { z_publisher_drop(z_publisher_move(&pub)); } assert(arg.called == true); assert(arg.dropped == true); z_task_join(z_task_move(&task)); z_publisher_drop(z_publisher_move(&pub)); z_subscriber_drop(z_subscriber_move(&sub)); z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&sample_handler)); #endif z_session_drop(z_session_move(&session2)); } #endif #endif #if Z_FEATURE_QUERYABLE == 1 && Z_FEATURE_QUERY == 1 #if Z_FEATURE_MULTI_THREAD == 1 void* get_from_another_thread(void* targ) { thread_arg_t* arg = (thread_arg_t*)targ; z_owned_closure_reply_t callback_reply; z_owned_fifo_handler_reply_t fifo_reply; assert(z_fifo_channel_reply_new(&callback_reply, &fifo_reply, 3) == Z_OK); assert(z_get(z_session_loan(arg->session), z_view_keyexpr_loan(arg->ke), "", z_closure_reply_move(&callback_reply), NULL) == Z_OK); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&fifo_reply)); return NULL; } #endif DECLARE_ON_RECEIVE_HANDLER(z_loaned_query_t, _ZP_NOTHING) void test_queryable_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_owned_queryable_t queryable; z_view_keyexpr_t ke; z_owned_closure_query_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/queryable_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/queryable_callback_drop") == Z_OK); } callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Queryable callback drop on undeclare: background = %d\n", background); z_closure_query(&closure, on_receive_z_loaned_query_t_handler, on_drop_z_loaned_query_t_handler, (void*)&arg); if (!background) { assert(z_declare_queryable(z_session_loan(&session1), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&closure), NULL) == Z_OK); } else { assert(z_declare_background_queryable(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_query_move(&closure), NULL) == Z_OK); } z_sleep_s(2); z_owned_closure_reply_t callback_reply; z_owned_fifo_handler_reply_t fifo_reply; assert(z_fifo_channel_reply_new(&callback_reply, &fifo_reply, 3) == Z_OK); assert(z_get(z_session_loan(&session2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&callback_reply), NULL) == Z_OK); z_sleep_s(1); if (!background) { z_undeclare_queryable(z_queryable_move(&queryable)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.called == true); assert(arg.dropped == true); #if Z_FEATURE_LOCAL_QUERYABLE == 1 && Z_FEATURE_MULTI_THREAD == 1 printf("Test: Local Queryable callback drop on undeclare, background = %d\n", background); arg.called = false; arg.dropped = false; z_closure_query(&closure, on_receive_z_loaned_query_t_handler, on_drop_z_loaned_query_t_handler, (void*)&arg); if (!background) { assert(z_declare_queryable(z_session_loan(&session2), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&closure), NULL) == Z_OK); } else { assert(z_declare_background_queryable(z_session_loan(&session2), z_view_keyexpr_loan(&ke), z_closure_query_move(&closure), NULL) == Z_OK); } thread_arg_t thread_arg = { .session = &session2, .ke = &ke, }; z_owned_task_t task; z_task_init(&task, NULL, get_from_another_thread, &thread_arg); z_sleep_s(1); if (!background) { z_undeclare_queryable(z_queryable_move(&queryable)); } else { z_close(z_session_loan_mut(&session2), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_task_join(z_task_move(&task)); #endif z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&fifo_reply)); } #if Z_FEATURE_MULTI_THREAD == 1 typedef struct query_thread_arg_t { z_owned_fifo_handler_query_t* receiver; z_view_keyexpr_t* ke; } query_thread_arg_t; void* reply_from_another_thread(void* targ) { query_thread_arg_t* arg = (query_thread_arg_t*)targ; z_owned_query_t query; assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(arg->receiver), &query) == Z_OK); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); z_query_reply(z_query_loan(&query), z_view_keyexpr_loan(arg->ke), z_bytes_move(&payload), NULL); z_query_drop(z_query_move(&query)); z_fifo_handler_query_drop(z_fifo_handler_query_move(arg->receiver)); return NULL; } #endif DECLARE_ON_RECEIVE_HANDLER(z_loaned_reply_t, _ZP_NOTHING) void test_querier_callback_drop_on_undeclare(bool background) { z_owned_session_t session1, session2; z_owned_queryable_t queryable; z_owned_querier_t querier; z_view_keyexpr_t ke; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); if (!background) { assert(z_view_keyexpr_from_str(&ke, "test/undeclare/querier_callback_drop") == Z_OK); } else { assert(z_view_keyexpr_from_str(&ke, "test/undeclare_background/querier_callback_drop") == Z_OK); } callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Querier callback drop on undeclare: background = %d\n", background); z_owned_closure_query_t query_closure; z_owned_fifo_handler_query_t query_handler; z_fifo_channel_query_new(&query_closure, &query_handler, 3); assert(z_declare_queryable(z_session_loan(&session2), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_closure), NULL) == Z_OK); z_owned_closure_reply_t reply_closure; z_closure_reply(&reply_closure, on_receive_z_loaned_reply_t_handler, on_drop_z_loaned_reply_t_handler, (void*)&arg); if (!background) { assert(z_declare_querier(z_session_loan(&session1), &querier, z_view_keyexpr_loan(&ke), NULL) == Z_OK); z_sleep_s(2); assert(z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_closure), NULL) == Z_OK); } else { z_sleep_s(2); assert(z_get(z_session_loan(&session1), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_closure), NULL) == Z_OK); } z_owned_query_t query; z_sleep_s(1); assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &query) == Z_OK); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "payload"); assert(z_query_reply(z_query_loan(&query), z_view_keyexpr_loan(&ke), z_bytes_move(&payload), NULL) == Z_OK); z_query_drop(z_query_move(&query)); z_sleep_s(1); if (!background) { z_undeclare_querier(z_querier_move(&querier)); } else { z_close(z_session_loan_mut(&session1), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_undeclare_queryable(z_queryable_move(&queryable)); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler)); #if Z_FEATURE_LOCAL_QUERYABLE == 1 && Z_FEATURE_MULTI_THREAD == 1 printf("Test: Local Querier callback drop on undeclare: background = %d\n", background); arg.called = false; arg.dropped = false; z_fifo_channel_query_new(&query_closure, &query_handler, 3); assert(z_declare_queryable(z_session_loan(&session2), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_closure), NULL) == Z_OK); z_closure_reply(&reply_closure, on_receive_z_loaned_reply_t_handler, on_drop_z_loaned_reply_t_handler, (void*)&arg); if (!background) { assert(z_declare_querier(z_session_loan(&session2), &querier, z_view_keyexpr_loan(&ke), NULL) == Z_OK); assert(z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_closure), NULL) == Z_OK); } else { assert(z_get(z_session_loan(&session2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_closure), NULL) == Z_OK); } query_thread_arg_t thread_arg = { .receiver = &query_handler, .ke = &ke, }; z_owned_task_t task; z_task_init(&task, NULL, reply_from_another_thread, &thread_arg); z_sleep_s(1); if (!background) { z_undeclare_querier(z_querier_move(&querier)); } else { z_close(z_session_loan_mut(&session2), NULL); } assert(arg.called == true); assert(arg.dropped == true); z_task_join(z_task_move(&task)); z_undeclare_queryable(z_queryable_move(&queryable)); #endif z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } #if Z_FEATURE_LIVELINESS == 1 void test_liveliness_get_callback_drop_on_undeclare(void) { z_owned_session_t session1, session2; z_view_keyexpr_t ke; z_owned_closure_reply_t closure; assert(open_session(&session1) == Z_OK); assert(open_session(&session2) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/undeclare/liveliness_get_callback_drop") == Z_OK); callback_arg_t arg; arg.called = false; arg.dropped = false; printf("Test: Liveliness Get callback drop on undeclare\n"); z_closure_reply(&closure, on_receive_z_loaned_reply_t_handler, on_drop_z_loaned_reply_t_handler, (void*)&arg); z_owned_liveliness_token_t token; assert(z_liveliness_declare_token(z_session_loan(&session2), &token, z_view_keyexpr_loan(&ke), NULL) == Z_OK); z_sleep_s(1); assert(z_liveliness_get(z_session_loan(&session1), z_view_keyexpr_loan(&ke), z_closure_reply_move(&closure), NULL) == Z_OK); z_sleep_s(1); z_close(z_session_loan_mut(&session1), NULL); assert(arg.called == true); assert(arg.dropped == true); z_liveliness_token_drop(z_liveliness_token_move(&token)); z_session_drop(z_session_move(&session1)); z_session_drop(z_session_move(&session2)); } #endif #endif void test_no_declare_after_session_close(void) { #if Z_FEATURE_SUBSCRIPTION == 1 { printf("Test: No subscriber can be declared after session close\n"); z_owned_session_t session; z_owned_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; callback_arg_t arg; arg.called = false; arg.dropped = false; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/subscriber") == Z_OK); z_close(z_session_loan_mut(&session), NULL); z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); assert(z_declare_subscriber(z_session_loan(&session), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif #if Z_FEATURE_QUERYABLE == 1 { printf("Test: No queryable can be declared after session close\n"); z_owned_session_t session; z_owned_queryable_t queryable; z_view_keyexpr_t ke; z_owned_closure_query_t closure; callback_arg_t arg; arg.called = false; arg.dropped = false; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/queryable") == Z_OK); z_close(z_session_loan_mut(&session), NULL); z_closure_query(&closure, on_receive_z_loaned_query_t_handler, on_drop_z_loaned_query_t_handler, (void*)&arg); assert(z_declare_queryable(z_session_loan(&session), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif #if Z_FEATURE_LIVELINESS == 1 #if Z_FEATURE_SUBSCRIPTION == 1 { printf("Test: No liveliness subscriber can be declared after session close\n"); z_owned_session_t session; z_owned_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/liveliness_subscriber") == Z_OK); z_close(z_session_loan_mut(&session), NULL); callback_arg_t arg; arg.called = false; arg.dropped = false; z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); assert(z_liveliness_declare_subscriber(z_session_loan(&session), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif #if Z_FEATURE_QUERY == 1 { printf("Test: No z_liveliness_get can be issued after session close\n"); z_owned_session_t session; z_view_keyexpr_t ke; z_owned_closure_reply_t closure; callback_arg_t arg; arg.called = false; arg.dropped = false; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/liveliness_get") == Z_OK); z_close(z_session_loan_mut(&session), NULL); z_closure_reply(&closure, on_receive_z_loaned_reply_t_handler, on_drop_z_loaned_reply_t_handler, (void*)&arg); assert(z_liveliness_get(z_session_loan(&session), z_view_keyexpr_loan(&ke), z_closure_reply_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif { printf("Test: No liveliness token can be declared after session close\n"); z_owned_session_t session; z_owned_liveliness_token_t token; z_view_keyexpr_t ke; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/liveliness_token") == Z_OK); z_close(z_session_loan_mut(&session), NULL); assert(z_liveliness_declare_token(z_session_loan(&session), &token, z_view_keyexpr_loan(&ke), NULL) == _Z_ERR_SESSION_CLOSED); z_session_drop(z_session_move(&session)); } #endif #if Z_FEATURE_QUERY == 1 { printf("Test: No z_get can be issued after session close\n"); z_owned_session_t session; z_view_keyexpr_t ke; z_owned_closure_reply_t closure; callback_arg_t arg; arg.called = false; arg.dropped = false; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/get") == Z_OK); z_close(z_session_loan_mut(&session), NULL); z_closure_reply(&closure, on_receive_z_loaned_reply_t_handler, on_drop_z_loaned_reply_t_handler, (void*)&arg); assert(z_get(z_session_loan(&session), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 { printf("Test: No advanced subscriber can be declared after session close\n"); z_owned_session_t session; ze_owned_advanced_subscriber_t subscriber; z_view_keyexpr_t ke; z_owned_closure_sample_t closure; callback_arg_t arg; arg.called = false; arg.dropped = false; assert(open_session(&session) == Z_OK); assert(z_view_keyexpr_from_str(&ke, "test/closed/advanced_subscriber") == Z_OK); z_close(z_session_loan_mut(&session), NULL); z_closure_sample(&closure, on_receive_z_loaned_sample_t_handler, on_drop_z_loaned_sample_t_handler, (void*)&arg); assert(ze_declare_advanced_subscriber(z_session_loan(&session), &subscriber, z_view_keyexpr_loan(&ke), z_closure_sample_move(&closure), NULL) == _Z_ERR_SESSION_CLOSED); assert(arg.called == false); assert(arg.dropped == true); z_session_drop(z_session_move(&session)); } #endif } #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_MULTI_THREAD == 1 DECLARE_ON_RECEIVE_HANDLER(z_loaned_transport_event_t, _ZP_NOTHING) void test_transport_events_callback_drop_on_undeclare(bool background) { z_owned_session_t session1; callback_arg_t arg; z_owned_closure_transport_event_t callback; z_transport_events_listener_options_t options; arg.called = false; arg.dropped = false; printf("Test: Transport events callback drop on undeclare: background = %d\n", background); assert(open_session(&session1) == Z_OK); z_closure_transport_event(&callback, on_receive_z_loaned_transport_event_t_handler, on_drop_z_loaned_transport_event_t_handler, (void*)&arg); z_transport_events_listener_options_default(&options); options.history = true; if (!background) { z_owned_transport_events_listener_t listener; assert(z_declare_transport_events_listener(z_session_loan(&session1), &listener, z_closure_transport_event_move(&callback), &options) == Z_OK); assert(z_undeclare_transport_events_listener(z_transport_events_listener_move(&listener)) == Z_OK); assert(arg.called == true); assert(arg.dropped == true); z_session_drop(z_session_move(&session1)); } else { assert(z_declare_background_transport_events_listener( z_session_loan(&session1), z_closure_transport_event_move(&callback), &options) == Z_OK); assert(z_close(z_session_loan_mut(&session1), NULL) == Z_OK); assert(arg.called == true); assert(arg.dropped == true); z_session_drop(z_session_move(&session1)); } } DECLARE_ON_RECEIVE_HANDLER(z_loaned_link_event_t, _ZP_NOTHING) void test_link_events_callback_drop_on_undeclare(bool background) { z_owned_session_t session1; callback_arg_t arg; z_owned_closure_link_event_t callback; z_link_events_listener_options_t options; arg.called = false; arg.dropped = false; printf("Test: Link events callback drop on undeclare: background = %d\n", background); assert(open_session(&session1) == Z_OK); z_closure_link_event(&callback, on_receive_z_loaned_link_event_t_handler, on_drop_z_loaned_link_event_t_handler, (void*)&arg); z_link_events_listener_options_default(&options); options.history = true; if (!background) { z_owned_link_events_listener_t listener; assert(z_declare_link_events_listener(z_session_loan(&session1), &listener, z_closure_link_event_move(&callback), &options) == Z_OK); assert(z_undeclare_link_events_listener(z_link_events_listener_move(&listener)) == Z_OK); assert(arg.called == true); assert(arg.dropped == true); z_session_drop(z_session_move(&session1)); } else { assert(z_declare_background_link_events_listener(z_session_loan(&session1), z_closure_link_event_move(&callback), &options) == Z_OK); assert(z_close(z_session_loan_mut(&session1), NULL) == Z_OK); assert(arg.called == true); assert(arg.dropped == true); z_session_drop(z_session_move(&session1)); } } #endif int main(void) { #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 test_subscriber_callback_drop_on_undeclare(false); test_subscriber_callback_drop_on_undeclare(true); #if Z_FEATURE_MATCHING == 1 test_matching_callback_drop_on_undeclare(false); test_matching_callback_drop_on_undeclare(true); #endif #if Z_FEATURE_LIVELINESS == 1 test_liveliness_subscriber_callback_drop_on_undeclare(false); test_liveliness_subscriber_callback_drop_on_undeclare(true); #endif #endif #if Z_FEATURE_QUERYABLE == 1 && Z_FEATURE_QUERY == 1 test_queryable_callback_drop_on_undeclare(false); test_queryable_callback_drop_on_undeclare(true); test_querier_callback_drop_on_undeclare(false); test_querier_callback_drop_on_undeclare(true); #if Z_FEATURE_LIVELINESS == 1 test_liveliness_get_callback_drop_on_undeclare(); #endif #endif #if Z_FEATURE_ADVANCED_SUBSCRIPTION == 1 && Z_FEATURE_ADVANCED_PUBLICATION == 1 test_advanced_subscriber_callback_drop_on_undeclare(false); test_advanced_subscriber_callback_drop_on_undeclare(true); test_advanced_subscriber_late_join_callback_drop_on_undeclare(false); test_advanced_subscriber_late_join_callback_drop_on_undeclare(true); #if Z_ADVANCED_PUBSUB_TEST_USE_TCP_PROXY == 1 test_advanced_sample_miss_callback_drop_on_undeclare(false); test_advanced_sample_miss_callback_drop_on_undeclare(true); #endif #endif #if Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_UNICAST_TRANSPORT == 1 && Z_FEATURE_MULTI_THREAD == 1 test_transport_events_callback_drop_on_undeclare(false); test_transport_events_callback_drop_on_undeclare(true); test_link_events_callback_drop_on_undeclare(false); test_link_events_callback_drop_on_undeclare(true); #endif test_no_declare_after_session_close(); return 0; } ================================================ FILE: tests/z_api_cancellation_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #undef NDEBUG #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 && defined(Z_FEATURE_UNSTABLE_API) void on_reply(z_loaned_reply_t* reply, void* arg) { uint32_t* t = (uint32_t*)arg; z_sleep_s(3); (*t) += 1; _ZP_UNUSED(reply); } void on_drop(void* arg) { uint32_t* t = (uint32_t*)arg; z_sleep_s(3); (*t) += 2; } void test_cancel_get(void) { printf("test_cancel_get\n"); const char* query_expr = "zenoh-pico/query/cancellation/test"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, query_expr); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_queryable_t queryable; z_owned_closure_query_t query_callback; z_owned_fifo_handler_query_t query_handler; z_fifo_channel_query_new(&query_callback, &query_handler, 16); z_declare_queryable(z_session_loan(&s1), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback), NULL); z_sleep_s(2); z_owned_closure_reply_t reply_callback; z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); printf("Check cancel removes callback\n"); z_owned_cancellation_token_t ct, ct_clone; assert(z_cancellation_token_new(&ct) == Z_OK); assert(!z_cancellation_token_is_cancelled(z_cancellation_token_loan(&ct))); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_get_options_t opts; z_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); assert(z_cancellation_token_cancel(z_cancellation_token_loan_mut(&ct)) == Z_OK); assert(z_cancellation_token_is_cancelled(z_cancellation_token_loan(&ct))); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_owned_reply_t reply; assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_owned_query_t q; assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_OK); z_query_drop(z_query_move(&q)); z_sleep_s(1); printf("Check cancel blocks until callback is finished\n"); uint32_t t = 0; assert(z_cancellation_token_new(&ct) == Z_OK); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_closure_reply(&reply_callback, on_reply, on_drop, (void*)&t); z_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_OK); z_owned_bytes_t b; z_bytes_from_static_str(&b, "ok"); z_query_reply(z_query_loan(&q), z_query_keyexpr(z_query_loan(&q)), z_bytes_move(&b), NULL); z_query_drop(z_query_move(&q)); z_sleep_s(1); assert(z_cancellation_token_cancel(z_cancellation_token_loan_mut(&ct)) == Z_OK); assert(t == 3); printf("Check cancelled token does not send a query\n"); z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); assert(z_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts) == Z_ERR_CANCELLED); assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_sleep_s(1); assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_CHANNEL_NODATA); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler)); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_queryable_drop(z_queryable_move(&queryable)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } void test_cancel_querier_get(void) { printf("test_cancel_querier_get\n"); const char* query_expr = "zenoh-pico/querier/cancellation/test"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, query_expr); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_queryable_t queryable; z_owned_closure_query_t query_callback; z_owned_fifo_handler_query_t query_handler; z_fifo_channel_query_new(&query_callback, &query_handler, 16); z_declare_queryable(z_session_loan(&s1), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback), NULL); z_owned_querier_t querier; z_declare_querier(z_session_loan(&s2), &querier, z_view_keyexpr_loan(&ke), NULL); z_sleep_s(2); z_owned_closure_reply_t reply_callback; z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); printf("Check cancel removes callback\n"); z_owned_cancellation_token_t ct, ct_clone; assert(z_cancellation_token_new(&ct) == Z_OK); assert(!z_cancellation_token_is_cancelled(z_cancellation_token_loan(&ct))); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_querier_get_options_t opts; z_querier_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); assert(z_cancellation_token_cancel(z_cancellation_token_loan_mut(&ct)) == Z_OK); assert(z_cancellation_token_is_cancelled(z_cancellation_token_loan(&ct))); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_owned_reply_t reply; assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_owned_query_t q; assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_OK); z_query_drop(z_query_move(&q)); z_sleep_s(1); printf("Check cancel blocks until callback is finished\n"); uint32_t t = 0; assert(z_cancellation_token_new(&ct) == Z_OK); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_querier_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_closure_reply(&reply_callback, on_reply, on_drop, (void*)&t); z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_OK); z_owned_bytes_t b; z_bytes_from_static_str(&b, "ok"); z_query_reply(z_query_loan(&q), z_query_keyexpr(z_query_loan(&q)), z_bytes_move(&b), NULL); z_query_drop(z_query_move(&q)); z_sleep_s(1); assert(z_cancellation_token_cancel(z_cancellation_token_loan_mut(&ct)) == Z_OK); assert(t == 3); printf("Check cancelled token does not send a query\n"); z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_querier_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); assert(z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_callback), &opts) == Z_ERR_CANCELLED); assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_sleep_s(1); assert(z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q) == Z_CHANNEL_NODATA); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler)); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_queryable_drop(z_queryable_move(&queryable)); z_querier_drop(z_querier_move(&querier)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } void test_cancel_does_not_prevent_session_close_on_drop(void) { printf("test_cancel_does_not_prevent_session_close_on_drop\n"); const char* query_expr = "zenoh-pico/cancellation_does_not_prevent_session_close_on_drop"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, query_expr); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_queryable_t queryable; z_owned_closure_query_t query_callback; z_owned_fifo_handler_query_t query_handler; z_fifo_channel_query_new(&query_callback, &query_handler, 16); z_declare_queryable(z_session_loan(&s1), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback), NULL); z_owned_querier_t querier; z_declare_querier(z_session_loan(&s2), &querier, z_view_keyexpr_loan(&ke), NULL); z_sleep_s(2); z_owned_closure_reply_t reply_callback, reply_callback2; z_owned_fifo_handler_reply_t reply_handler, reply_handler2; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); z_fifo_channel_reply_new(&reply_callback2, &reply_handler2, 16); z_owned_cancellation_token_t ct, ct_clone, ct_clone2; assert(z_cancellation_token_new(&ct) == Z_OK); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); assert(z_cancellation_token_clone(&ct_clone2, z_cancellation_token_loan(&ct)) == Z_OK); z_get_options_t opts; z_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts); z_querier_get_options_t opts2; z_querier_get_options_default(&opts2); opts2.cancellation_token = z_cancellation_token_move(&ct_clone2); z_querier_get(z_querier_loan(&querier), "", z_closure_reply_move(&reply_callback2), &opts2); z_session_drop(z_session_move(&s2)); z_owned_reply_t reply; assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler2), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler2)); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler)); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_queryable_drop(z_queryable_move(&queryable)); z_querier_drop(z_querier_move(&querier)); z_session_drop(z_session_move(&s1)); } #if Z_FEATURE_LIVELINESS == 1 void test_liveliness_get(void) { printf("test_cancel_liveliness_get\n"); const char* query_expr = "zenoh-pico/liveliness_query/cancellation/test"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, query_expr); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_liveliness_token_t token; z_liveliness_declare_token(z_session_loan(&s1), &token, z_view_keyexpr_loan(&ke), NULL); z_sleep_s(2); z_owned_closure_reply_t reply_callback; printf("Check cancel blocks until callback is finished\n"); z_owned_cancellation_token_t ct, ct_clone; uint32_t t = 0; assert(z_cancellation_token_new(&ct) == Z_OK); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_liveliness_get_options_t opts; z_liveliness_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); z_closure_reply(&reply_callback, on_reply, on_drop, (void*)&t); z_liveliness_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); assert(z_cancellation_token_cancel(z_cancellation_token_loan_mut(&ct)) == Z_OK); assert(t == 3); printf("Check cancelled token does not send a query\n"); z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); assert(z_cancellation_token_clone(&ct_clone, z_cancellation_token_loan(&ct)) == Z_OK); z_liveliness_get_options_default(&opts); opts.cancellation_token = z_cancellation_token_move(&ct_clone); assert(z_liveliness_get(z_session_loan(&s2), z_view_keyexpr_loan(&ke), z_closure_reply_move(&reply_callback), &opts) == Z_ERR_CANCELLED); z_owned_reply_t reply; assert(z_fifo_handler_reply_try_recv(z_fifo_handler_reply_loan(&reply_handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_cancellation_token_drop(z_cancellation_token_move(&ct)); z_liveliness_token_drop(z_liveliness_token_move(&token)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #endif #endif int main(void) { #if Z_FEATURE_QUERY == 1 && Z_FEATURE_MULTI_THREAD == 1 && defined(Z_FEATURE_UNSTABLE_API) test_cancel_get(); test_cancel_querier_get(); test_cancel_does_not_prevent_session_close_on_drop(); #if Z_FEATURE_LIVELINESS == 1 test_liveliness_get(); #endif #endif return 0; } ================================================ FILE: tests/z_api_connectivity_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #if defined(Z_FEATURE_UNSTABLE_API) && Z_FEATURE_CONNECTIVITY == 1 && Z_FEATURE_UNICAST_TRANSPORT == 1 && \ Z_FEATURE_LINK_TCP == 1 && Z_FEATURE_MULTI_THREAD == 1 #undef NDEBUG #include #define assert_ok(x) \ { \ int ret = (int)x; \ if (ret != Z_OK) { \ printf("%s failed: %d\n", #x, ret); \ assert(false); \ } \ } typedef struct events_ctx_t { atomic_uint transport_put; atomic_uint transport_delete; atomic_uint link_put; atomic_uint link_delete; } events_ctx_t; typedef struct transport_events_ctx_t { atomic_uint put; atomic_uint delete_; atomic_bool put_zid_set; atomic_bool delete_zid_set; atomic_bool put_snapshot_set; z_id_t put_zid; z_id_t delete_zid; z_whatami_t put_whatami; bool put_is_qos; bool put_is_multicast; bool put_is_shm; } transport_events_ctx_t; typedef struct link_events_ctx_t { atomic_uint put; atomic_uint delete_; atomic_bool put_zid_set; atomic_bool delete_zid_set; atomic_bool put_snapshot_set; z_id_t put_zid; z_id_t delete_zid; bool put_src_non_empty; bool put_dst_non_empty; uint16_t put_mtu; bool put_is_streamed; bool put_is_reliable; } link_events_ctx_t; typedef struct transport_capture_t { z_owned_transport_t transport; unsigned count; } transport_capture_t; typedef struct transport_info_capture_t { unsigned count; z_id_t first_zid; bool has_first; } transport_info_capture_t; typedef struct link_info_capture_t { unsigned count; z_id_t first_zid; bool has_first; bool first_src_non_empty; bool first_dst_non_empty; } link_info_capture_t; static void events_ctx_reset(events_ctx_t *ctx) { atomic_store_explicit(&ctx->transport_put, 0, memory_order_relaxed); atomic_store_explicit(&ctx->transport_delete, 0, memory_order_relaxed); atomic_store_explicit(&ctx->link_put, 0, memory_order_relaxed); atomic_store_explicit(&ctx->link_delete, 0, memory_order_relaxed); } static void transport_events_ctx_reset(transport_events_ctx_t *ctx) { atomic_store_explicit(&ctx->put, 0, memory_order_relaxed); atomic_store_explicit(&ctx->delete_, 0, memory_order_relaxed); atomic_store_explicit(&ctx->put_zid_set, false, memory_order_relaxed); atomic_store_explicit(&ctx->delete_zid_set, false, memory_order_relaxed); atomic_store_explicit(&ctx->put_snapshot_set, false, memory_order_relaxed); ctx->put_whatami = Z_WHATAMI_CLIENT; ctx->put_is_qos = false; ctx->put_is_multicast = false; ctx->put_is_shm = false; } static void link_events_ctx_reset(link_events_ctx_t *ctx) { atomic_store_explicit(&ctx->put, 0, memory_order_relaxed); atomic_store_explicit(&ctx->delete_, 0, memory_order_relaxed); atomic_store_explicit(&ctx->put_zid_set, false, memory_order_relaxed); atomic_store_explicit(&ctx->delete_zid_set, false, memory_order_relaxed); atomic_store_explicit(&ctx->put_snapshot_set, false, memory_order_relaxed); ctx->put_src_non_empty = false; ctx->put_dst_non_empty = false; ctx->put_mtu = 0; ctx->put_is_streamed = false; ctx->put_is_reliable = false; } static bool wait_counter_at_least(atomic_uint *counter, unsigned expected) { for (unsigned i = 0; i < 50; ++i) { if (atomic_load_explicit(counter, memory_order_acquire) >= expected) { return true; } z_sleep_ms(100); } return false; } static void open_listener_session(z_owned_session_t *session, const char *listen_locator) { z_owned_config_t cfg; z_config_default(&cfg); assert_ok(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_MODE_KEY, "peer")); assert_ok(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_LISTEN_KEY, listen_locator)); assert_ok(z_open(session, z_config_move(&cfg), NULL)); } static void open_connector_session(z_owned_session_t *session, const char *connect_locator) { z_owned_config_t cfg; z_config_default(&cfg); assert_ok(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_MODE_KEY, "peer")); assert_ok(zp_config_insert(z_config_loan_mut(&cfg), Z_CONFIG_CONNECT_KEY, connect_locator)); assert_ok(z_open(session, z_config_move(&cfg), NULL)); } static void close_session_with_tasks(z_owned_session_t *session) { z_session_drop(z_session_move(session)); } static void on_transport_event(z_loaned_transport_event_t *event, void *arg) { transport_events_ctx_t *ctx = (transport_events_ctx_t *)arg; const z_loaned_transport_t *transport = z_transport_event_transport(event); z_id_t zid = z_transport_zid(transport); switch (z_transport_event_kind(event)) { case Z_SAMPLE_KIND_PUT: if (!atomic_load_explicit(&ctx->put_zid_set, memory_order_relaxed)) { ctx->put_zid = zid; atomic_store_explicit(&ctx->put_zid_set, true, memory_order_release); } if (!atomic_load_explicit(&ctx->put_snapshot_set, memory_order_relaxed)) { ctx->put_whatami = z_transport_whatami(transport); ctx->put_is_qos = z_transport_is_qos(transport); ctx->put_is_multicast = z_transport_is_multicast(transport); ctx->put_is_shm = z_transport_is_shm(transport); atomic_store_explicit(&ctx->put_snapshot_set, true, memory_order_release); } atomic_fetch_add_explicit(&ctx->put, 1, memory_order_release); break; case Z_SAMPLE_KIND_DELETE: if (!atomic_load_explicit(&ctx->delete_zid_set, memory_order_relaxed)) { ctx->delete_zid = zid; atomic_store_explicit(&ctx->delete_zid_set, true, memory_order_release); } atomic_fetch_add_explicit(&ctx->delete_, 1, memory_order_release); break; default: break; } } static void on_link_event(z_loaned_link_event_t *event, void *arg) { link_events_ctx_t *ctx = (link_events_ctx_t *)arg; const z_loaned_link_t *link = z_link_event_link(event); z_id_t zid = z_link_zid(link); switch (z_link_event_kind(event)) { case Z_SAMPLE_KIND_PUT: if (!atomic_load_explicit(&ctx->put_zid_set, memory_order_relaxed)) { ctx->put_zid = zid; atomic_store_explicit(&ctx->put_zid_set, true, memory_order_release); } if (!atomic_load_explicit(&ctx->put_snapshot_set, memory_order_relaxed)) { z_owned_string_t src; z_owned_string_t dst; z_internal_string_null(&src); z_internal_string_null(&dst); assert_ok(z_link_src(link, &src)); assert_ok(z_link_dst(link, &dst)); ctx->put_src_non_empty = z_string_len(z_loan(src)) > 0; ctx->put_dst_non_empty = z_string_len(z_loan(dst)) > 0; z_string_drop(z_string_move(&src)); z_string_drop(z_string_move(&dst)); ctx->put_mtu = z_link_mtu(link); ctx->put_is_streamed = z_link_is_streamed(link); ctx->put_is_reliable = z_link_is_reliable(link); atomic_store_explicit(&ctx->put_snapshot_set, true, memory_order_release); } atomic_fetch_add_explicit(&ctx->put, 1, memory_order_release); break; case Z_SAMPLE_KIND_DELETE: if (!atomic_load_explicit(&ctx->delete_zid_set, memory_order_relaxed)) { ctx->delete_zid = zid; atomic_store_explicit(&ctx->delete_zid_set, true, memory_order_release); } atomic_fetch_add_explicit(&ctx->delete_, 1, memory_order_release); break; default: break; } } static void on_link_event_count_only(z_loaned_link_event_t *event, void *arg) { events_ctx_t *ctx = (events_ctx_t *)arg; switch (z_link_event_kind(event)) { case Z_SAMPLE_KIND_PUT: atomic_fetch_add_explicit(&ctx->link_put, 1, memory_order_relaxed); break; case Z_SAMPLE_KIND_DELETE: atomic_fetch_add_explicit(&ctx->link_delete, 1, memory_order_relaxed); break; default: break; } } static void capture_transport(z_loaned_transport_t *transport, void *arg) { transport_capture_t *ctx = (transport_capture_t *)arg; ctx->count++; if (ctx->count == 1) { assert_ok(z_transport_clone(&ctx->transport, transport)); } } static void capture_transport_info(z_loaned_transport_t *transport, void *arg) { transport_info_capture_t *ctx = (transport_info_capture_t *)arg; ctx->count++; if (!ctx->has_first) { ctx->first_zid = z_transport_zid(transport); ctx->has_first = true; } } static void capture_link_info(z_loaned_link_t *link, void *arg) { link_info_capture_t *ctx = (link_info_capture_t *)arg; ctx->count++; if (!ctx->has_first) { z_owned_string_t src; z_owned_string_t dst; z_internal_string_null(&src); z_internal_string_null(&dst); assert_ok(z_link_src(link, &src)); assert_ok(z_link_dst(link, &dst)); ctx->first_zid = z_link_zid(link); ctx->has_first = true; ctx->first_src_non_empty = z_string_len(z_loan(src)) > 0; ctx->first_dst_non_empty = z_string_len(z_loan(dst)) > 0; z_string_drop(z_string_move(&src)); z_string_drop(z_string_move(&dst)); } } static unsigned capture_single_transport(const z_loaned_session_t *session, z_owned_transport_t *out) { transport_capture_t ctx; z_internal_transport_null(&ctx.transport); ctx.count = 0; z_owned_closure_transport_t callback; assert_ok(z_closure_transport(&callback, capture_transport, NULL, &ctx)); assert_ok(z_info_transports(session, z_closure_transport_move(&callback))); z_internal_transport_null(out); if (ctx.count > 0) { z_transport_take(out, z_transport_move(&ctx.transport)); } return ctx.count; } static void test_info_transports_and_links(void) { printf("test_info_transports_and_links\n"); z_owned_session_t s1, s2; open_listener_session(&s1, "tcp/127.0.0.1:7452"); open_connector_session(&s2, "tcp/127.0.0.1:7452"); z_sleep_s(1); z_id_t s2_zid = z_info_zid(z_session_loan(&s2)); transport_info_capture_t transport_ctx = {0}; z_owned_closure_transport_t transport_callback; assert_ok(z_closure_transport(&transport_callback, capture_transport_info, NULL, &transport_ctx)); assert_ok(z_info_transports(z_session_loan(&s1), z_closure_transport_move(&transport_callback))); assert(transport_ctx.count == 1); assert(transport_ctx.has_first); assert(memcmp(&transport_ctx.first_zid, &s2_zid, sizeof(z_id_t)) == 0); link_info_capture_t link_ctx = {0}; z_owned_closure_link_t link_callback; assert_ok(z_closure_link(&link_callback, capture_link_info, NULL, &link_ctx)); assert_ok(z_info_links(z_session_loan(&s1), z_closure_link_move(&link_callback), NULL)); assert(link_ctx.count == 1); assert(link_ctx.has_first); assert(memcmp(&link_ctx.first_zid, &s2_zid, sizeof(z_id_t)) == 0); assert(link_ctx.first_src_non_empty); assert(link_ctx.first_dst_non_empty); close_session_with_tasks(&s2); close_session_with_tasks(&s1); } static void test_info_links_filtered(void) { printf("test_info_links_filtered\n"); z_owned_session_t s1, s2; open_listener_session(&s1, "tcp/127.0.0.1:7453"); open_connector_session(&s2, "tcp/127.0.0.1:7453"); z_sleep_s(1); z_id_t s2_zid = z_info_zid(z_session_loan(&s2)); z_owned_transport_t s1_transport; z_owned_transport_t s2_transport; z_internal_transport_null(&s1_transport); z_internal_transport_null(&s2_transport); assert(capture_single_transport(z_session_loan(&s1), &s1_transport) == 1); assert(capture_single_transport(z_session_loan(&s2), &s2_transport) == 1); assert(z_internal_transport_check(&s1_transport)); assert(z_internal_transport_check(&s2_transport)); link_info_capture_t match_ctx = {0}; z_owned_closure_link_t match_callback; assert_ok(z_closure_link(&match_callback, capture_link_info, NULL, &match_ctx)); z_info_links_options_t match_opts; z_info_links_options_default(&match_opts); match_opts.transport = z_transport_move(&s1_transport); assert_ok(z_info_links(z_session_loan(&s1), z_closure_link_move(&match_callback), &match_opts)); assert(match_ctx.count == 1); assert(match_ctx.has_first); assert(memcmp(&match_ctx.first_zid, &s2_zid, sizeof(z_id_t)) == 0); assert(match_ctx.first_src_non_empty); assert(match_ctx.first_dst_non_empty); link_info_capture_t miss_ctx = {0}; z_owned_closure_link_t miss_callback; assert_ok(z_closure_link(&miss_callback, capture_link_info, NULL, &miss_ctx)); z_info_links_options_t miss_opts; z_info_links_options_default(&miss_opts); miss_opts.transport = z_transport_move(&s2_transport); assert_ok(z_info_links(z_session_loan(&s1), z_closure_link_move(&miss_callback), &miss_opts)); assert(miss_ctx.count == 0); assert(!miss_ctx.has_first); z_transport_drop(z_transport_move(&s1_transport)); z_transport_drop(z_transport_move(&s2_transport)); close_session_with_tasks(&s2); close_session_with_tasks(&s1); } static void test_info_transports_and_links_empty(void) { printf("test_info_transports_and_links_empty\n"); z_owned_session_t s1; open_listener_session(&s1, "tcp/127.0.0.1:7460"); z_sleep_ms(250); transport_info_capture_t transport_ctx = {0}; z_owned_closure_transport_t transport_callback; assert_ok(z_closure_transport(&transport_callback, capture_transport_info, NULL, &transport_ctx)); assert_ok(z_info_transports(z_session_loan(&s1), z_closure_transport_move(&transport_callback))); assert(transport_ctx.count == 0); assert(!transport_ctx.has_first); link_info_capture_t link_ctx = {0}; z_owned_closure_link_t link_callback; assert_ok(z_closure_link(&link_callback, capture_link_info, NULL, &link_ctx)); assert_ok(z_info_links(z_session_loan(&s1), z_closure_link_move(&link_callback), NULL)); assert(link_ctx.count == 0); assert(!link_ctx.has_first); close_session_with_tasks(&s1); } static void run_transport_events_test(const char *locator, bool history, bool background) { z_owned_session_t s1, s2; transport_events_ctx_t ctx; transport_events_ctx_reset(&ctx); open_listener_session(&s1, locator); if (history) { open_connector_session(&s2, locator); z_sleep_s(1); } z_owned_closure_transport_event_t callback; assert_ok(z_closure_transport_event(&callback, on_transport_event, NULL, &ctx)); z_transport_events_listener_options_t opts; z_transport_events_listener_options_default(&opts); opts.history = history; z_owned_transport_events_listener_t listener; if (background) { assert_ok(z_declare_background_transport_events_listener(z_session_loan(&s1), z_closure_transport_event_move(&callback), &opts)); } else { assert_ok(z_declare_transport_events_listener(z_session_loan(&s1), &listener, z_closure_transport_event_move(&callback), &opts)); } if (!history) { assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 0); open_connector_session(&s2, locator); } z_id_t s2_zid = z_info_zid(z_session_loan(&s2)); assert(wait_counter_at_least(&ctx.put, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.put_zid_set, memory_order_acquire)); assert(atomic_load_explicit(&ctx.put_snapshot_set, memory_order_acquire)); assert(memcmp(&ctx.put_zid, &s2_zid, sizeof(z_id_t)) == 0); assert(ctx.put_whatami == Z_WHATAMI_PEER); assert(!ctx.put_is_qos); assert(!ctx.put_is_multicast); assert(!ctx.put_is_shm); close_session_with_tasks(&s2); assert(wait_counter_at_least(&ctx.delete_, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.delete_zid_set, memory_order_acquire)); assert(memcmp(&ctx.delete_zid, &s2_zid, sizeof(z_id_t)) == 0); if (!background) { assert_ok(z_undeclare_transport_events_listener(z_transport_events_listener_move(&listener))); } close_session_with_tasks(&s1); } static void run_link_events_test(const char *locator, bool history, bool background) { z_owned_session_t s1, s2; link_events_ctx_t ctx; link_events_ctx_reset(&ctx); open_listener_session(&s1, locator); if (history) { open_connector_session(&s2, locator); z_sleep_s(1); } z_owned_closure_link_event_t callback; assert_ok(z_closure_link_event(&callback, on_link_event, NULL, &ctx)); z_link_events_listener_options_t opts; z_link_events_listener_options_default(&opts); opts.history = history; z_owned_link_events_listener_t listener; if (background) { assert_ok(z_declare_background_link_events_listener(z_session_loan(&s1), z_closure_link_event_move(&callback), &opts)); } else { assert_ok(z_declare_link_events_listener(z_session_loan(&s1), &listener, z_closure_link_event_move(&callback), &opts)); } if (!history) { assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 0); open_connector_session(&s2, locator); } z_id_t s2_zid = z_info_zid(z_session_loan(&s2)); assert(wait_counter_at_least(&ctx.put, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.put_zid_set, memory_order_acquire)); assert(atomic_load_explicit(&ctx.put_snapshot_set, memory_order_acquire)); assert(memcmp(&ctx.put_zid, &s2_zid, sizeof(z_id_t)) == 0); assert(ctx.put_src_non_empty); assert(ctx.put_dst_non_empty); assert(ctx.put_mtu > 0); assert(ctx.put_is_streamed); assert(ctx.put_is_reliable); close_session_with_tasks(&s2); assert(wait_counter_at_least(&ctx.delete_, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.delete_zid_set, memory_order_acquire)); assert(memcmp(&ctx.delete_zid, &s2_zid, sizeof(z_id_t)) == 0); if (!background) { assert_ok(z_undeclare_link_events_listener(z_link_events_listener_move(&listener))); } close_session_with_tasks(&s1); } static void test_transport_events_no_history(void) { printf("test_transport_events_no_history\n"); run_transport_events_test("tcp/127.0.0.1:7448", false, false); } static void test_transport_events_history(void) { printf("test_transport_events_history\n"); run_transport_events_test("tcp/127.0.0.1:7449", true, false); } static void test_transport_events_background(void) { printf("test_transport_events_background\n"); run_transport_events_test("tcp/127.0.0.1:7450", false, true); } static void test_transport_events_background_history(void) { printf("test_transport_events_background_history\n"); run_transport_events_test("tcp/127.0.0.1:7461", true, true); } static void test_link_events_no_history(void) { printf("test_link_events_no_history\n"); run_link_events_test("tcp/127.0.0.1:7451", false, false); } static void test_link_events_history(void) { printf("test_link_events_history\n"); run_link_events_test("tcp/127.0.0.1:7454", true, false); } static void test_link_events_background(void) { printf("test_link_events_background\n"); run_link_events_test("tcp/127.0.0.1:7455", false, true); } static void test_link_events_background_history(void) { printf("test_link_events_background_history\n"); run_link_events_test("tcp/127.0.0.1:7462", true, true); } static void test_transport_events_undeclare(void) { printf("test_transport_events_undeclare\n"); z_owned_session_t s1, s2, s3; transport_events_ctx_t ctx; transport_events_ctx_reset(&ctx); open_listener_session(&s1, "tcp/127.0.0.1:7463"); z_owned_closure_transport_event_t callback; assert_ok(z_closure_transport_event(&callback, on_transport_event, NULL, &ctx)); z_owned_transport_events_listener_t listener; assert_ok(z_declare_transport_events_listener(z_session_loan(&s1), &listener, z_closure_transport_event_move(&callback), NULL)); open_connector_session(&s2, "tcp/127.0.0.1:7463"); assert(wait_counter_at_least(&ctx.put, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert_ok(z_undeclare_transport_events_listener(z_transport_events_listener_move(&listener))); close_session_with_tasks(&s2); z_sleep_ms(500); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 0); open_connector_session(&s3, "tcp/127.0.0.1:7463"); z_sleep_ms(500); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 0); close_session_with_tasks(&s3); close_session_with_tasks(&s1); } static void test_link_events_undeclare(void) { printf("test_link_events_undeclare\n"); z_owned_session_t s1, s2, s3; link_events_ctx_t ctx; link_events_ctx_reset(&ctx); open_listener_session(&s1, "tcp/127.0.0.1:7464"); z_owned_closure_link_event_t callback; assert_ok(z_closure_link_event(&callback, on_link_event, NULL, &ctx)); z_owned_link_events_listener_t listener; assert_ok( z_declare_link_events_listener(z_session_loan(&s1), &listener, z_closure_link_event_move(&callback), NULL)); open_connector_session(&s2, "tcp/127.0.0.1:7464"); assert(wait_counter_at_least(&ctx.put, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert_ok(z_undeclare_link_events_listener(z_link_events_listener_move(&listener))); close_session_with_tasks(&s2); z_sleep_ms(500); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 0); open_connector_session(&s3, "tcp/127.0.0.1:7464"); z_sleep_ms(500); assert(atomic_load_explicit(&ctx.put, memory_order_acquire) == 1); assert(atomic_load_explicit(&ctx.delete_, memory_order_acquire) == 0); close_session_with_tasks(&s3); close_session_with_tasks(&s1); } static void test_link_events_filtered(void) { printf("test_link_events_filtered\n"); z_owned_session_t s1, s2; open_listener_session(&s1, "tcp/127.0.0.1:7456"); open_connector_session(&s2, "tcp/127.0.0.1:7456"); z_sleep_s(1); z_owned_transport_t s1_transport; z_owned_transport_t s2_transport; z_internal_transport_null(&s1_transport); z_internal_transport_null(&s2_transport); assert(capture_single_transport(z_session_loan(&s1), &s1_transport) == 1); assert(capture_single_transport(z_session_loan(&s2), &s2_transport) == 1); assert(z_internal_transport_check(&s1_transport)); assert(z_internal_transport_check(&s2_transport)); events_ctx_t match_ctx; events_ctx_reset(&match_ctx); z_owned_closure_link_event_t match_callback; assert_ok(z_closure_link_event(&match_callback, on_link_event_count_only, NULL, &match_ctx)); z_link_events_listener_options_t match_opts; z_link_events_listener_options_default(&match_opts); match_opts.history = true; match_opts.transport = z_transport_move(&s1_transport); z_owned_link_events_listener_t match_listener; assert_ok(z_declare_link_events_listener(z_session_loan(&s1), &match_listener, z_closure_link_event_move(&match_callback), &match_opts)); assert(wait_counter_at_least(&match_ctx.link_put, 1)); z_sleep_ms(250); assert(atomic_load_explicit(&match_ctx.link_put, memory_order_acquire) == 1); assert_ok(z_undeclare_link_events_listener(z_link_events_listener_move(&match_listener))); events_ctx_t miss_ctx; events_ctx_reset(&miss_ctx); z_owned_closure_link_event_t miss_callback; assert_ok(z_closure_link_event(&miss_callback, on_link_event_count_only, NULL, &miss_ctx)); z_link_events_listener_options_t miss_opts; z_link_events_listener_options_default(&miss_opts); miss_opts.history = true; miss_opts.transport = z_transport_move(&s2_transport); z_owned_link_events_listener_t miss_listener; assert_ok(z_declare_link_events_listener(z_session_loan(&s1), &miss_listener, z_closure_link_event_move(&miss_callback), &miss_opts)); z_sleep_ms(500); assert(atomic_load_explicit(&miss_ctx.link_put, memory_order_acquire) == 0); assert(atomic_load_explicit(&miss_ctx.link_delete, memory_order_acquire) == 0); assert_ok(z_undeclare_link_events_listener(z_link_events_listener_move(&miss_listener))); z_transport_drop(z_transport_move(&s1_transport)); z_transport_drop(z_transport_move(&s2_transport)); close_session_with_tasks(&s2); close_session_with_tasks(&s1); } int main(int argc, char **argv) { (void)argc; (void)argv; test_info_transports_and_links(); test_info_links_filtered(); test_info_transports_and_links_empty(); test_transport_events_no_history(); test_transport_events_history(); test_transport_events_background(); test_transport_events_background_history(); test_transport_events_undeclare(); test_link_events_no_history(); test_link_events_history(); test_link_events_background(); test_link_events_background_history(); test_link_events_undeclare(); test_link_events_filtered(); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; return 0; } #endif ================================================ FILE: tests/z_api_double_drop_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include "zenoh-pico.h" #undef NDEBUG #include #define URL "demo/example" void test_keyexpr(void) { z_owned_keyexpr_t keyexpr; z_keyexpr_from_str(&keyexpr, URL); assert(z_internal_check(keyexpr)); z_drop(z_move(keyexpr)); assert(!z_internal_check(keyexpr)); z_drop(z_move(keyexpr)); assert(!z_internal_check(keyexpr)); } void test_config(void) { z_owned_config_t config; z_config_default(&config); assert(z_internal_check(config)); z_drop(z_move(config)); assert(!z_internal_check(config)); z_drop(z_move(config)); assert(!z_internal_check(config)); } int main(void) { test_keyexpr(); test_config(); return 0; } ================================================ FILE: tests/z_api_encoding_test.c ================================================ #include #include #include #include "zenoh-pico/api/encoding.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #undef NDEBUG #include void test_null_encoding(void) { z_owned_encoding_t e; z_internal_encoding_null(&e); assert(!z_internal_encoding_check(&e)); z_encoding_drop(z_encoding_move(&e)); } void test_encoding_without_id(void) { z_owned_encoding_t e1; z_encoding_from_str(&e1, "my_encoding"); assert(z_internal_encoding_check(&e1)); z_owned_string_t s; z_encoding_to_string(z_encoding_loan(&e1), &s); assert(strncmp("zenoh/bytes;my_encoding", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e1)); z_string_drop(z_string_move(&s)); z_owned_encoding_t e2; z_encoding_from_substr(&e2, "my_encoding", 4); assert(z_internal_encoding_check(&e2)); z_encoding_to_string(z_encoding_loan(&e2), &s); assert(strncmp("zenoh/bytes;my_e", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e2)); z_string_drop(z_string_move(&s)); } void test_encoding_with_id(void) { z_owned_encoding_t e1; z_encoding_from_str(&e1, "zenoh/string;utf8"); assert(z_internal_encoding_check(&e1)); z_owned_string_t s; z_encoding_to_string(z_encoding_loan(&e1), &s); assert(strncmp("zenoh/string;utf8", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e1)); z_string_drop(z_string_move(&s)); z_owned_encoding_t e2; z_encoding_from_substr(&e2, "zenoh/string;utf8", 15); assert(z_internal_encoding_check(&e2)); z_encoding_to_string(z_encoding_loan(&e2), &s); assert(strncmp("zenoh/string;utf8", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e2)); z_string_drop(z_string_move(&s)); z_owned_encoding_t e3; z_encoding_from_str(&e3, "custom_id;custom_schema"); assert(z_internal_encoding_check(&e3)); z_encoding_to_string(z_encoding_loan(&e3), &s); assert(strncmp("zenoh/bytes;custom_id;custom_schema", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e3)); z_string_drop(z_string_move(&s)); z_owned_encoding_t e4; z_encoding_from_substr(&e4, "custom_id;custom_schema", 16); assert(z_internal_encoding_check(&e2)); z_encoding_to_string(z_encoding_loan(&e4), &s); assert(strncmp("zenoh/bytes;custom_id;custom", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e4)); z_string_drop(z_string_move(&s)); } void test_with_schema(void) { z_owned_encoding_t e; z_internal_encoding_null(&e); z_encoding_set_schema_from_str(z_encoding_loan_mut(&e), "my_schema"); z_owned_string_t s; z_encoding_to_string(z_encoding_loan_mut(&e), &s); assert(strncmp("zenoh/bytes;my_schema", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e)); z_string_drop(z_string_move(&s)); z_encoding_from_str(&e, "zenoh/string;"); z_encoding_set_schema_from_substr(z_encoding_loan_mut(&e), "my_schema", 3); z_encoding_to_string(z_encoding_loan(&e), &s); assert(strncmp("zenoh/string;my_", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_encoding_drop(z_encoding_move(&e)); z_string_drop(z_string_move(&s)); } void test_constants(void) { #if Z_FEATURE_ENCODING_VALUES == 1 z_owned_string_t s; z_encoding_to_string(z_encoding_zenoh_bytes(), &s); assert(strncmp("zenoh/bytes", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_string_drop(z_string_move(&s)); z_encoding_to_string(z_encoding_zenoh_string(), &s); assert(strncmp("zenoh/string", z_string_data(z_string_loan(&s)), z_string_len(z_string_loan(&s))) == 0); z_string_drop(z_string_move(&s)); #endif } void test_equals(void) { #if Z_FEATURE_ENCODING_VALUES == 1 z_owned_encoding_t e; z_encoding_from_str(&e, "zenoh/string"); assert(z_encoding_equals(z_encoding_loan(&e), z_encoding_zenoh_string())); assert(!z_encoding_equals(z_encoding_loan(&e), z_encoding_zenoh_serialized())); z_encoding_drop(z_encoding_move(&e)); #endif } int main(void) { test_null_encoding(); test_encoding_without_id(); test_encoding_with_id(); test_with_schema(); test_constants(); test_equals(); } ================================================ FILE: tests/z_api_liveliness_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/liveliness.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #if Z_FEATURE_LIVELINESS == 1 && Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_QUERY == 1 #undef NDEBUG #include typedef struct context_t { bool token1_put; bool token2_put; bool token1_drop; bool token2_drop; } context_t; #define assert_ok(x) \ { \ int ret = (int)x; \ if (ret != Z_OK) { \ printf("%s failed: %d\n", #x, ret); \ assert(false); \ } \ } const char* token1_expr = "zenoh-pico/liveliness/test/1"; const char* token2_expr = "zenoh-pico/liveliness/test/2"; void on_receive(z_loaned_sample_t* s, void* context) { context_t* c = (context_t*)context; const z_loaned_keyexpr_t* k = z_sample_keyexpr(s); z_view_string_t ks; z_keyexpr_as_view_string(k, &ks); if (z_sample_kind(s) == Z_SAMPLE_KIND_PUT) { if (strncmp(token1_expr, z_string_data(z_view_string_loan(&ks)), z_string_len(z_view_string_loan(&ks))) == 0) { c->token1_put = true; } else if (strncmp(token2_expr, z_string_data(z_view_string_loan(&ks)), z_string_len(z_view_string_loan(&ks))) == 0) { c->token2_put = true; } } else if (z_sample_kind(s) == Z_SAMPLE_KIND_DELETE) { if (strncmp(token1_expr, z_string_data(z_view_string_loan(&ks)), z_string_len(z_view_string_loan(&ks))) == 0) { c->token1_drop = true; } else if (strncmp(token2_expr, z_string_data(z_view_string_loan(&ks)), z_string_len(z_view_string_loan(&ks))) == 0) { c->token2_drop = true; } } } void test_liveliness_sub(bool multicast, bool history) { printf("test_liveliness_sub: multicast=%d, history=%d\n", multicast, history); const char* expr = "zenoh-pico/liveliness/test/*"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k, k1, k2; z_view_keyexpr_from_str(&k, expr); z_view_keyexpr_from_str(&k1, token1_expr); z_view_keyexpr_from_str(&k2, token2_expr); if (multicast) { zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "client"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); } assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_owned_liveliness_token_t t1, t2; // In history mode we can declare token before subscribing if (history) { assert_ok(z_liveliness_declare_token(z_session_loan(&s2), &t1, z_view_keyexpr_loan(&k1), NULL)); assert_ok(z_liveliness_declare_token(z_session_loan(&s2), &t2, z_view_keyexpr_loan(&k2), NULL)); } z_sleep_s(1); z_owned_closure_sample_t closure; context_t context = {false, false, false, false}; z_closure_sample(&closure, on_receive, NULL, (void*)(&context)); z_owned_subscriber_t sub; z_liveliness_subscriber_options_t sub_opt; z_liveliness_subscriber_options_default(&sub_opt); sub_opt.history = history; assert_ok(z_liveliness_declare_subscriber(z_session_loan(&s1), &sub, z_view_keyexpr_loan(&k), z_closure_sample_move(&closure), &sub_opt)); z_sleep_s(1); if (!history) { assert_ok(z_liveliness_declare_token(z_session_loan(&s2), &t1, z_view_keyexpr_loan(&k1), NULL)); assert_ok(z_liveliness_declare_token(z_session_loan(&s2), &t2, z_view_keyexpr_loan(&k2), NULL)); } z_sleep_s(1); assert(context.token1_put); assert(context.token2_put); assert_ok(z_liveliness_undeclare_token(z_liveliness_token_move(&t1))); z_sleep_s(1); assert(context.token1_drop); assert(!context.token2_drop); assert_ok(z_liveliness_undeclare_token(z_liveliness_token_move(&t2))); z_sleep_s(1); assert(context.token2_drop); z_closure_sample_drop(z_closure_sample_move(&closure)); z_subscriber_drop(z_subscriber_move(&sub)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } void test_liveliness_get(void) { const char* expr = "zenoh-pico/liveliness/test/*"; z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k, k1; z_view_keyexpr_from_str(&k, expr); z_view_keyexpr_from_str(&k1, token1_expr); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_sleep_s(1); z_owned_liveliness_token_t t1; assert_ok(z_liveliness_declare_token(z_session_loan(&s1), &t1, z_view_keyexpr_loan(&k1), NULL)); z_sleep_s(1); z_owned_fifo_handler_reply_t handler; z_owned_closure_reply_t cb; assert_ok(z_fifo_channel_reply_new(&cb, &handler, 3)); assert_ok(z_liveliness_get(z_session_loan(&s2), z_view_keyexpr_loan(&k), z_closure_reply_move(&cb), NULL)); z_owned_reply_t reply; assert_ok(z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&handler), &reply)); assert(z_reply_is_ok(z_reply_loan(&reply))); const z_loaned_keyexpr_t* reply_keyexpr = z_sample_keyexpr(z_reply_ok(z_reply_loan(&reply))); z_view_string_t reply_keyexpr_s; z_keyexpr_as_view_string(reply_keyexpr, &reply_keyexpr_s); assert(strlen(token1_expr) == z_string_len(z_view_string_loan(&reply_keyexpr_s))); assert(strncmp(token1_expr, z_string_data(z_view_string_loan(&reply_keyexpr_s)), z_string_len(z_view_string_loan(&reply_keyexpr_s))) == 0); z_reply_drop(z_reply_move(&reply)); assert(z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&handler), &reply) == Z_CHANNEL_DISCONNECTED); z_liveliness_token_drop(z_liveliness_token_move(&t1)); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&handler)); z_sleep_s(1); assert_ok(z_fifo_channel_reply_new(&cb, &handler, 3)); z_liveliness_get(z_session_loan(&s2), z_view_keyexpr_loan(&k), z_closure_reply_move(&cb), NULL); assert(z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&handler), &reply) == Z_CHANNEL_DISCONNECTED); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&handler)); z_closure_reply_drop(z_closure_reply_move(&cb)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } int main(int argc, char** argv) { (void)argc; (void)argv; #if defined(ZENOH_LINUX) test_liveliness_sub(true, false); test_liveliness_sub(true, true); #endif test_liveliness_sub(false, false); test_liveliness_sub(false, true); test_liveliness_get(); } #else int main(int argc, char** argv) { (void)argc; (void)argv; } #endif ================================================ FILE: tests/z_api_local_queryable_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico.h" #undef NDEBUG #include #if Z_FEATURE_QUERY == 1 && Z_FEATURE_QUERYABLE == 1 && Z_FEATURE_LOCAL_QUERYABLE == 1 && Z_FEATURE_MULTI_THREAD == 1 static const char *QUERYABLE_EXPR = "zenoh-pico/locality/query"; void test_queryable(z_locality_t get_allowed_destination, z_locality_t q1_allowed_origin, z_locality_t q2_allowed_origin) { printf("Testing Quryables: get_dest=%d q1_origin=%d q2_origin=%d\n", get_allowed_destination, q1_allowed_origin, q2_allowed_origin); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, QUERYABLE_EXPR); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_queryable_t queryable1, queryable2; z_queryable_options_t opts1, opts2; z_queryable_options_default(&opts1); z_queryable_options_default(&opts2); opts1.allowed_origin = q1_allowed_origin; opts2.allowed_origin = q2_allowed_origin; z_owned_closure_query_t query_callback1, query_callback2; z_owned_fifo_handler_query_t query_handler1, query_handler2; z_fifo_channel_query_new(&query_callback1, &query_handler1, 16); z_declare_queryable(z_session_loan(&s1), &queryable1, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback1), &opts1); z_fifo_channel_query_new(&query_callback2, &query_handler2, 16); z_declare_queryable(z_session_loan(&s2), &queryable2, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback2), &opts2); z_owned_closure_reply_t reply_callback; z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); z_get_options_t opts; z_get_options_default(&opts); opts.allowed_destination = get_allowed_destination; opts.consolidation = z_query_consolidation_none(); z_sleep_s(1); z_get(z_session_loan(&s1), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts); z_sleep_s(1); z_owned_query_t q1, q2; z_result_t ret1 = z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler1), &q1); z_result_t ret2 = z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler2), &q2); bool expect_1 = false; bool expect_2 = false; size_t expect_responses = 0; if (_z_locality_allows_local(get_allowed_destination) && _z_locality_allows_local(q1_allowed_origin)) { assert(ret1 == Z_OK); z_owned_bytes_t payload1; z_bytes_copy_from_str(&payload1, "1"); z_query_reply(z_query_loan(&q1), z_view_keyexpr_loan(&ke), z_bytes_move(&payload1), NULL); z_query_drop(z_query_move(&q1)); expect_1 = true; expect_responses++; } else { assert(ret1 != Z_OK); } if (_z_locality_allows_remote(get_allowed_destination) && _z_locality_allows_remote(q2_allowed_origin)) { assert(ret2 == Z_OK); z_owned_bytes_t payload2; z_bytes_copy_from_str(&payload2, "2"); z_query_reply(z_query_loan(&q2), z_view_keyexpr_loan(&ke), z_bytes_move(&payload2), NULL); z_query_drop(z_query_move(&q2)); expect_2 = true; expect_responses++; } else { assert(ret2 != Z_OK); } bool found_1 = false; bool found_2 = false; size_t reply_count = 0; z_sleep_s(1); z_owned_reply_t reply; for (z_result_t res = z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&reply_handler), &reply); res != Z_CHANNEL_DISCONNECTED; res = z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&reply_handler), &reply)) { assert(z_reply_is_ok(z_loan(reply))); const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); // SAFETY: test. // Flawfinder: ignore [CWE-126] if (strncmp(z_string_data(z_loan(replystr)), "1", 1) == 0) { found_1 = true; } // SAFETY: test. // Flawfinder: ignore [CWE-126] if (strncmp(z_string_data(z_loan(replystr)), "2", 1) == 0) { found_2 = true; } reply_count++; // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(keystr)), QUERYABLE_EXPR, strlen(QUERYABLE_EXPR)) == 0); z_reply_drop(z_reply_move(&reply)); z_string_drop(z_string_move(&replystr)); } assert(reply_count == expect_responses); assert(found_1 == expect_1); assert(found_2 == expect_2); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler1)); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler2)); z_queryable_drop(z_queryable_move(&queryable1)); z_queryable_drop(z_queryable_move(&queryable2)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } void test_queryable_peer_mode(z_locality_t get_allowed_destination, z_locality_t q_allowed_origin) { printf("Testing peer mode: get_dest=%d q_origin=%d\n", get_allowed_destination, q_allowed_origin); z_owned_session_t s; z_owned_config_t c; z_config_default(&c); assert(zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer") == Z_OK); assert(zp_config_insert(z_loan_mut(c), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:10000") == Z_OK); assert(zp_config_insert(z_loan_mut(c), Z_CONFIG_MULTICAST_SCOUTING_KEY, "false") == Z_OK); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, QUERYABLE_EXPR); assert(z_open(&s, z_config_move(&c), NULL) == Z_OK); z_owned_queryable_t queryable; z_queryable_options_t qopts; z_queryable_options_default(&qopts); qopts.allowed_origin = q_allowed_origin; z_owned_closure_query_t query_callback; z_owned_fifo_handler_query_t query_handler; z_fifo_channel_query_new(&query_callback, &query_handler, 16); z_declare_queryable(z_session_loan(&s), &queryable, z_view_keyexpr_loan(&ke), z_closure_query_move(&query_callback), &qopts); z_owned_closure_reply_t reply_callback; z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_reply_new(&reply_callback, &reply_handler, 16); z_get_options_t opts; z_get_options_default(&opts); opts.allowed_destination = get_allowed_destination; z_get(z_session_loan(&s), z_view_keyexpr_loan(&ke), "", z_closure_reply_move(&reply_callback), &opts); z_owned_query_t q; z_result_t ret = z_fifo_handler_query_try_recv(z_fifo_handler_query_loan(&query_handler), &q); bool expect_response = _z_locality_allows_local(get_allowed_destination) && _z_locality_allows_local(q_allowed_origin); size_t expect_responses = 0; if (expect_response) { assert(ret == Z_OK); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, "peer_reply"); z_query_reply(z_query_loan(&q), z_view_keyexpr_loan(&ke), z_bytes_move(&payload), NULL); z_query_drop(z_query_move(&q)); expect_responses = 1; } else { assert(ret != Z_OK); } size_t reply_count = 0; bool found_reply = false; z_sleep_s(1); z_owned_reply_t reply; for (z_result_t res = z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&reply_handler), &reply); res != Z_CHANNEL_DISCONNECTED; res = z_fifo_handler_reply_recv(z_fifo_handler_reply_loan(&reply_handler), &reply)) { assert(z_reply_is_ok(z_loan(reply))); const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); // SAFETY: test. // Flawfinder: ignore [CWE-126] if (strncmp(z_string_data(z_loan(replystr)), "peer_reply", 10) == 0) { found_reply = true; } reply_count++; // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(keystr)), QUERYABLE_EXPR, strlen(QUERYABLE_EXPR)) == 0); z_reply_drop(z_reply_move(&reply)); z_string_drop(z_string_move(&replystr)); } assert(reply_count == expect_responses); assert(found_reply == expect_response); z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&reply_handler)); z_fifo_handler_query_drop(z_fifo_handler_query_move(&query_handler)); z_queryable_drop(z_queryable_move(&queryable)); z_session_drop(z_session_move(&s)); } int main(int argc, char **argv) { (void)argc; (void)argv; z_locality_t localities[] = {Z_LOCALITY_ANY, Z_LOCALITY_REMOTE, Z_LOCALITY_SESSION_LOCAL}; for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { for (size_t k = 0; k < 3; k++) { test_queryable(localities[i], localities[j], localities[k]); } } } // Test local query on a single session in peer mode test_queryable_peer_mode(Z_LOCALITY_ANY, Z_LOCALITY_ANY); } #else int main(int argc, char** argv) { (void)argc; (void)argv; } #endif ================================================ FILE: tests/z_api_local_subscriber_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include "zenoh-pico.h" #undef NDEBUG #include #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_LOCAL_SUBSCRIBER == 1 && \ Z_FEATURE_MULTI_THREAD == 1 static const char *PUB_EXPR = "zenoh-pico/locality/pub-sub"; static const char *PAYLOAD = "payload"; void test_put_sub(z_locality_t pub_allowed_destination, z_locality_t s1_allowed_origin, z_locality_t s2_allowed_origin) { printf("Testing: %d %d %d\n", pub_allowed_destination, s1_allowed_origin, s2_allowed_origin); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, PUB_EXPR); assert(z_open(&s1, z_config_move(&c1), NULL) == Z_OK); assert(z_open(&s2, z_config_move(&c2), NULL) == Z_OK); z_owned_subscriber_t subscriber1, subscriber2; z_subscriber_options_t opts1, opts2; z_subscriber_options_default(&opts1); z_subscriber_options_default(&opts2); opts1.allowed_origin = s1_allowed_origin; opts2.allowed_origin = s2_allowed_origin; z_owned_closure_sample_t subscriber_callback1, subscriber_callback2; z_owned_fifo_handler_sample_t subscriber_handler1, subscriber_handler2; z_fifo_channel_sample_new(&subscriber_callback1, &subscriber_handler1, 16); z_declare_subscriber(z_session_loan(&s1), &subscriber1, z_view_keyexpr_loan(&ke), z_closure_sample_move(&subscriber_callback1), &opts1); z_fifo_channel_sample_new(&subscriber_callback2, &subscriber_handler2, 16); z_declare_subscriber(z_session_loan(&s2), &subscriber2, z_view_keyexpr_loan(&ke), z_closure_sample_move(&subscriber_callback2), &opts2); z_put_options_t opts; z_put_options_default(&opts); opts.allowed_destination = pub_allowed_destination; z_sleep_s(1); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, PAYLOAD); z_put(z_session_loan(&s1), z_view_keyexpr_loan(&ke), z_bytes_move(&payload), &opts); z_sleep_s(1); z_owned_sample_t sample1, sample2; z_result_t ret1 = z_fifo_handler_sample_try_recv(z_fifo_handler_sample_loan(&subscriber_handler1), &sample1); z_result_t ret2 = z_fifo_handler_sample_try_recv(z_fifo_handler_sample_loan(&subscriber_handler2), &sample2); if (_z_locality_allows_local(pub_allowed_destination) && _z_locality_allows_local(s1_allowed_origin)) { assert(ret1 == Z_OK); const z_loaned_sample_t *sample = z_sample_loan(&sample1); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t payloadstr; z_bytes_to_string(z_sample_payload(sample), &payloadstr); // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(payloadstr)), PAYLOAD, strlen(PAYLOAD)) == 0); // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(keystr)), PUB_EXPR, strlen(PUB_EXPR)) == 0); z_string_drop(z_string_move(&payloadstr)); z_sample_drop(z_sample_move(&sample1)); } else { assert(ret1 != Z_OK); } if (_z_locality_allows_remote(pub_allowed_destination) && _z_locality_allows_remote(s2_allowed_origin)) { assert(ret2 == Z_OK); const z_loaned_sample_t *sample = z_sample_loan(&sample2); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t payloadstr; z_bytes_to_string(z_sample_payload(sample), &payloadstr); // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(payloadstr)), PAYLOAD, strlen(PAYLOAD)) == 0); // SAFETY: test. // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(keystr)), PUB_EXPR, strlen(PUB_EXPR)) == 0); z_string_drop(z_string_move(&payloadstr)); z_sample_drop(z_sample_move(&sample2)); } else { assert(ret2 != Z_OK); } z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&subscriber_handler1)); z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&subscriber_handler2)); z_subscriber_drop(z_subscriber_move(&subscriber1)); z_subscriber_drop(z_subscriber_move(&subscriber2)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } int main(int argc, char **argv) { (void)argc; (void)argv; z_locality_t localities[] = {Z_LOCALITY_ANY, Z_LOCALITY_REMOTE, Z_LOCALITY_SESSION_LOCAL}; for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { for (size_t k = 0; k < 3; k++) { test_put_sub(localities[i], localities[j], localities[k]); } } } } #else int main(int argc, char** argv) { (void)argc; (void)argv; } #endif ================================================ FILE: tests/z_api_matching_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_MATCHING == 1 && Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 #undef NDEBUG #include static const char *PUB_EXPR = "zenoh-pico/matching/test/val"; static const char *SUB_EXPR = "zenoh-pico/matching/**"; static const char *KEY_EXPR_WRONG = "zenoh-pico/matching/test_wrong/*"; static const char *QUERYABLE_EXPR = "zenoh-pico/matching/query_test/val"; static const char *QUERIER_EXPR = "zenoh-pico/matching/query_test/*"; static const char *QUERYABLE_EXPR_WILD = "zenoh-pico/matching/query_test/**"; static unsigned long DEFAULT_TIMEOUT_S = 10; typedef enum { NONE, MATCH, UNMATCH, DROP } context_state_t; typedef struct context_t { #if Z_FEATURE_MULTI_THREAD == 1 z_owned_condvar_t cv; z_owned_mutex_t m; #else z_loaned_session_t *s1; z_loaned_session_t *s2; #endif context_state_t state; } context_t; static void _context_init(context_t *c) { #if Z_FEATURE_MULTI_THREAD == 1 z_condvar_init(&c->cv); z_mutex_init(&c->m); #endif c->state = NONE; } static void _context_drop(context_t *c) { #if Z_FEATURE_MULTI_THREAD == 1 z_condvar_drop(z_condvar_move(&c->cv)); z_mutex_drop(z_mutex_move(&c->m)); #else (void)c; #endif } #if Z_FEATURE_MULTI_THREAD == 1 static bool _context_wait(context_t *c, context_state_t state, unsigned long timeout_s) { z_mutex_lock(z_mutex_loan_mut(&c->m)); if (c->state != state) { printf("Waiting for state %d...\n", state); #ifdef ZENOH_MACOS _ZP_UNUSED(timeout_s); z_condvar_wait(z_condvar_loan_mut(&c->cv), z_mutex_loan_mut(&c->m)); #else z_clock_t clock = z_clock_now(); z_clock_advance_s(&clock, timeout_s); z_result_t res = z_condvar_wait_until(z_condvar_loan_mut(&c->cv), z_mutex_loan_mut(&c->m), &clock); if (res == Z_ETIMEDOUT) { fprintf(stderr, "Timeout waiting for state %d\n", state); return false; } #endif if (c->state != state) { fprintf(stderr, "Expected state %d, got %d\n", state, c->state); return false; } } c->state = NONE; z_mutex_unlock(z_mutex_loan_mut(&c->m)); return true; } static bool _context_wait_none(context_t *c, unsigned long timeout_s) { z_sleep_s(timeout_s); z_mutex_lock(z_mutex_loan_mut(&c->m)); context_state_t s = c->state; z_mutex_unlock(z_mutex_loan_mut(&c->m)); if (s != NONE) { fprintf(stderr, "Expected state %d, got %d\n", NONE, s); return false; } return true; } #else static bool _context_wait_none(context_t *c, unsigned long timeout_s) { unsigned long tm = timeout_s * 1000; while (c->state == NONE && tm > 0) { zp_spin_once(c->s1); zp_spin_once(c->s2); z_sleep_ms(100); tm -= 100; } if (c->state != NONE) { fprintf(stderr, "Expected state %d, got %d\n", NONE, c->state); return false; } return true; } static bool _context_wait(context_t *c, context_state_t state, unsigned long timeout_s) { unsigned long tm = timeout_s * 1000; while (c->state == NONE && tm > 0) { zp_spin_once(c->s1); zp_spin_once(c->s2); z_sleep_ms(100); tm -= 100; } if (tm <= 0) { fprintf(stderr, "Timeout waiting for state %d\n", state); return false; } if (c->state != state) { fprintf(stderr, "Expected state %d, got %d\n", state, c->state); return false; } c->state = NONE; return true; } #endif static void _context_notify(context_t *c, context_state_t state) { #if Z_FEATURE_MULTI_THREAD == 1 z_mutex_lock(z_mutex_loan_mut(&c->m)); #endif if (c->state != NONE) { fprintf(stderr, "State already set %d\n", c->state); assert(false); } c->state = state; fprintf(stderr, "State recieved %d\n", state); #if Z_FEATURE_MULTI_THREAD == 1 z_condvar_signal(z_condvar_loan_mut(&c->cv)); z_mutex_unlock(z_mutex_loan_mut(&c->m)); #endif } #define assert_ok(x) \ { \ int ret = (int)x; \ if (ret != Z_OK) { \ fprintf(stderr, "%s failed: %d\n", #x, ret); \ assert(false); \ } \ } void on_receive(const z_matching_status_t *s, void *context) { context_t *c = (context_t *)context; _context_notify(c, s->matching ? MATCH : UNMATCH); } void on_drop(void *context) { context_t *c = (context_t *)context; _context_notify(c, DROP); } void test_matching_listener_publisher(bool background, bool history) { printf("test_matching_listener_publisher: background=%d, history=%d\n", background, history); context_t context = {0}; _context_init(&context); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_sub, k_pub; z_view_keyexpr_from_str(&k_sub, SUB_EXPR); z_view_keyexpr_from_str(&k_pub, PUB_EXPR); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); #if Z_FEATURE_MULTI_THREAD == 0 context.s1 = z_loan_mut(s1); context.s2 = z_loan_mut(s2); #endif z_owned_publisher_t pub; assert_ok(z_declare_publisher(z_session_loan(&s1), &pub, z_view_keyexpr_loan(&k_pub), NULL)); z_owned_closure_matching_status_t closure; z_closure_matching_status(&closure, on_receive, on_drop, (void *)(&context)); z_owned_matching_listener_t matching_listener; z_owned_subscriber_t sub, sub2; z_owned_closure_sample_t callback, callback2; z_closure_sample(&callback, NULL, NULL, NULL); z_closure_sample(&callback2, NULL, NULL, NULL); if (history) { assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub, z_view_keyexpr_loan(&k_pub), z_closure_sample_move(&callback), NULL)); z_sleep_s(3); if (background) { assert_ok(z_publisher_declare_background_matching_listener(z_publisher_loan(&pub), z_closure_matching_status_move(&closure))); } else { assert_ok(z_publisher_declare_matching_listener(z_publisher_loan(&pub), &matching_listener, z_closure_matching_status_move(&closure))); } } else { if (background) { assert_ok(z_publisher_declare_background_matching_listener(z_publisher_loan(&pub), z_closure_matching_status_move(&closure))); } else { assert_ok(z_publisher_declare_matching_listener(z_publisher_loan(&pub), &matching_listener, z_closure_matching_status_move(&closure))); } assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub, z_view_keyexpr_loan(&k_pub), z_closure_sample_move(&callback), NULL)); } assert(_context_wait(&context, MATCH, DEFAULT_TIMEOUT_S)); assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub2, z_view_keyexpr_loan(&k_sub), z_closure_sample_move(&callback2), NULL)); z_sleep_s(1); assert(_context_wait_none(&context, 1)); z_subscriber_drop(z_subscriber_move(&sub)); assert(_context_wait_none(&context, 1)); z_subscriber_drop(z_subscriber_move(&sub2)); assert(_context_wait(&context, UNMATCH, DEFAULT_TIMEOUT_S)); z_publisher_drop(z_publisher_move(&pub)); assert(_context_wait(&context, DROP, DEFAULT_TIMEOUT_S)); if (!background) { z_matching_listener_drop(z_matching_listener_move(&matching_listener)); } z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); _context_drop(&context); } void test_matching_listener_querier(bool complete, bool background) { printf("test_matching_listener_querier: complete=%d, background=%d\n", complete, background); context_t context = {0}; _context_init(&context); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_queryable, k_querier, k_queryable_wild, k_queryable_wrong; z_view_keyexpr_from_str(&k_queryable, QUERYABLE_EXPR); z_view_keyexpr_from_str(&k_queryable_wild, QUERYABLE_EXPR_WILD); z_view_keyexpr_from_str(&k_querier, QUERIER_EXPR); z_view_keyexpr_from_str(&k_queryable_wrong, KEY_EXPR_WRONG); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); #if Z_FEATURE_MULTI_THREAD == 0 context.s1 = z_loan_mut(s1); context.s2 = z_loan_mut(s2); #endif z_querier_options_t querier_opts; z_querier_options_default(&querier_opts); querier_opts.target = complete ? Z_QUERY_TARGET_ALL_COMPLETE : Z_QUERY_TARGET_BEST_MATCHING; z_owned_querier_t querier; assert_ok(z_declare_querier(z_session_loan(&s1), &querier, z_view_keyexpr_loan(&k_querier), &querier_opts)); z_owned_closure_matching_status_t closure; z_closure_matching_status(&closure, on_receive, on_drop, (void *)(&context)); z_owned_matching_listener_t matching_listener; if (background) { assert_ok(z_querier_declare_background_matching_listener(z_querier_loan(&querier), z_closure_matching_status_move(&closure))); } else { assert_ok(z_querier_declare_matching_listener(z_querier_loan(&querier), &matching_listener, z_closure_matching_status_move(&closure))); } z_sleep_s(1); assert(_context_wait_none(&context, 1)); z_owned_queryable_t queryable_wrong; z_owned_closure_query_t callback_wrong; z_closure_query(&callback_wrong, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable_wrong, z_view_keyexpr_loan(&k_queryable_wrong), z_closure_query_move(&callback_wrong), NULL)); assert(_context_wait_none(&context, 1)); z_queryable_options_t queryable_options; z_queryable_options_default(&queryable_options); queryable_options.complete = false; z_owned_queryable_t queryable1, queryable2, queryable3; z_owned_closure_query_t callback1, callback2, callback3; z_closure_query(&callback1, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable1, z_view_keyexpr_loan(&k_queryable), z_closure_query_move(&callback1), &queryable_options)); if (complete) { assert(_context_wait_none(&context, 1)); } else { assert(_context_wait(&context, MATCH, DEFAULT_TIMEOUT_S)); } queryable_options.complete = false; z_closure_query(&callback2, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable2, z_view_keyexpr_loan(&k_queryable_wild), z_closure_query_move(&callback2), &queryable_options)); assert(_context_wait_none(&context, 1)); queryable_options.complete = true; z_closure_query(&callback3, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable3, z_view_keyexpr_loan(&k_queryable_wild), z_closure_query_move(&callback3), &queryable_options)); if (!complete) { assert(_context_wait_none(&context, 1)); } else { assert(_context_wait(&context, MATCH, DEFAULT_TIMEOUT_S)); } z_queryable_drop(z_queryable_move(&queryable2)); assert(_context_wait_none(&context, 1)); z_queryable_drop(z_queryable_move(&queryable3)); if (complete) { assert(_context_wait(&context, UNMATCH, DEFAULT_TIMEOUT_S)); } else { assert(_context_wait_none(&context, 1)); } z_queryable_drop(z_queryable_move(&queryable1)); if (!complete) { assert(_context_wait(&context, UNMATCH, DEFAULT_TIMEOUT_S)); } else { assert(_context_wait_none(&context, 1)); } z_querier_drop(z_querier_move(&querier)); z_queryable_drop(z_queryable_move(&queryable_wrong)); assert(_context_wait(&context, DROP, DEFAULT_TIMEOUT_S)); if (!background) { z_matching_listener_drop(z_matching_listener_move(&matching_listener)); } z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } static bool _check_publisher_status(z_owned_publisher_t *pub, z_loaned_session_t *s1, z_loaned_session_t *s2, bool expected) { z_matching_status_t status; status.matching = !expected; z_clock_t clock = z_clock_now(); while (status.matching != expected && z_clock_elapsed_s(&clock) < DEFAULT_TIMEOUT_S) { assert_ok(z_publisher_get_matching_status(z_publisher_loan(pub), &status)); z_sleep_ms(100); #if Z_FEATURE_MULTI_THREAD == 1 (void)s1; (void)s2; #else zp_spin_once(s1); zp_spin_once(s2); #endif } if (status.matching != expected) { fprintf(stderr, "Expected matching status %d, got %d\n", expected, status.matching); return false; } return true; } void test_matching_status_publisher(void) { printf("test_matching_status_publisher\n"); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_sub, k_pub, k_sub_wrong; z_view_keyexpr_from_str(&k_sub, SUB_EXPR); z_view_keyexpr_from_str(&k_pub, PUB_EXPR); z_view_keyexpr_from_str(&k_sub_wrong, KEY_EXPR_WRONG); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_owned_publisher_t pub; assert_ok(z_declare_publisher(z_session_loan(&s1), &pub, z_view_keyexpr_loan(&k_pub), NULL)); z_sleep_s(1); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), false)); z_owned_subscriber_t sub_wrong; z_owned_closure_sample_t callback_wrong; z_closure_sample(&callback_wrong, NULL, NULL, NULL); assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub_wrong, z_view_keyexpr_loan(&k_sub_wrong), z_closure_sample_move(&callback_wrong), NULL)); z_sleep_s(1); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), false)); z_owned_subscriber_t sub; z_owned_closure_sample_t callback; z_closure_sample(&callback, NULL, NULL, NULL); assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub, z_view_keyexpr_loan(&k_sub), z_closure_sample_move(&callback), NULL)); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), true)); z_owned_subscriber_t sub2; z_owned_closure_sample_t callback2; z_closure_sample(&callback2, NULL, NULL, NULL); assert_ok(z_declare_subscriber(z_session_loan(&s2), &sub2, z_view_keyexpr_loan(&k_pub), z_closure_sample_move(&callback), NULL)); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), true)); z_subscriber_drop(z_subscriber_move(&sub)); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), true)); z_subscriber_drop(z_subscriber_move(&sub2)); assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), false)); z_publisher_drop(z_publisher_move(&pub)); z_subscriber_drop(z_subscriber_move(&sub_wrong)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 static void test_matching_status_publisher_locality(z_locality_t locality, bool create_local_sub, bool create_remote_sub, bool expected) { printf("test_matching_status_publisher_locality locality=%d local=%d remote=%d expected=%d\n", (int)locality, create_local_sub, create_remote_sub, expected); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_pub, k_sub; z_view_keyexpr_from_str(&k_pub, PUB_EXPR); z_view_keyexpr_from_str(&k_sub, SUB_EXPR); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_publisher_options_t pub_opts; z_publisher_options_default(&pub_opts); pub_opts.allowed_destination = locality; z_owned_publisher_t pub; assert_ok(z_declare_publisher(z_session_loan(&s1), &pub, z_view_keyexpr_loan(&k_pub), &pub_opts)); z_owned_subscriber_t local_sub; z_owned_subscriber_t remote_sub; if (create_local_sub) { z_subscriber_options_t opts; z_subscriber_options_default(&opts); opts.allowed_origin = Z_LOCALITY_SESSION_LOCAL; z_owned_closure_sample_t cb; z_closure_sample(&cb, NULL, NULL, NULL); assert_ok(z_declare_subscriber(z_session_loan(&s1), &local_sub, z_view_keyexpr_loan(&k_sub), z_closure_sample_move(&cb), &opts)); } if (create_remote_sub) { z_owned_closure_sample_t cb; z_closure_sample(&cb, NULL, NULL, NULL); assert_ok(z_declare_subscriber(z_session_loan(&s2), &remote_sub, z_view_keyexpr_loan(&k_sub), z_closure_sample_move(&cb), NULL)); } assert(_check_publisher_status(&pub, z_loan_mut(s1), z_loan_mut(s2), expected)); if (create_local_sub) { z_subscriber_drop(z_subscriber_move(&local_sub)); } if (create_remote_sub) { z_subscriber_drop(z_subscriber_move(&remote_sub)); } z_publisher_drop(z_publisher_move(&pub)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #endif // Z_FEATURE_LOCAL_SUBSCRIBER == 1 static bool _check_querier_status(z_owned_querier_t *querier, z_loaned_session_t *s1, z_loaned_session_t *s2, bool expected) { z_matching_status_t status; status.matching = !expected; z_clock_t clock = z_clock_now(); while (status.matching != expected && z_clock_elapsed_s(&clock) < DEFAULT_TIMEOUT_S) { assert_ok(z_querier_get_matching_status(z_querier_loan(querier), &status)); #if Z_FEATURE_MULTI_THREAD == 1 (void)s1; (void)s2; #else zp_spin_once(s1); zp_spin_once(s2); #endif z_sleep_ms(100); } if (status.matching != expected) { fprintf(stderr, "Expected matching status %d, got %d\n", expected, status.matching); return false; } return true; } void test_matching_status_querier(bool complete) { printf("test_matching_status_querier: complete=%d\n", complete); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_queryable, k_querier, k_queryable_wrong, k_queryable_wild; z_view_keyexpr_from_str(&k_queryable_wild, QUERYABLE_EXPR_WILD); z_view_keyexpr_from_str(&k_queryable, QUERYABLE_EXPR); z_view_keyexpr_from_str(&k_querier, QUERIER_EXPR); z_view_keyexpr_from_str(&k_queryable_wrong, KEY_EXPR_WRONG); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_querier_options_t querier_opts; z_querier_options_default(&querier_opts); querier_opts.target = complete ? Z_QUERY_TARGET_ALL_COMPLETE : Z_QUERY_TARGET_BEST_MATCHING; z_owned_querier_t querier; assert_ok(z_declare_querier(z_session_loan(&s1), &querier, z_view_keyexpr_loan(&k_querier), &querier_opts)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), false); z_owned_queryable_t queryable_wrong; z_owned_closure_query_t callback_wrong; z_closure_query(&callback_wrong, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable_wrong, z_view_keyexpr_loan(&k_queryable_wrong), z_closure_query_move(&callback_wrong), NULL)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), false); z_queryable_options_t queryable_options; z_queryable_options_default(&queryable_options); queryable_options.complete = false; z_owned_queryable_t queryable1, queryable2, queryable3; z_owned_closure_query_t callback1, callback2, callback3; z_closure_query(&callback1, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable1, z_view_keyexpr_loan(&k_queryable), z_closure_query_move(&callback1), &queryable_options)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), !complete); queryable_options.complete = false; z_closure_query(&callback2, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable2, z_view_keyexpr_loan(&k_queryable_wild), z_closure_query_move(&callback2), &queryable_options)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), !complete); queryable_options.complete = true; z_closure_query(&callback3, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &queryable3, z_view_keyexpr_loan(&k_queryable_wild), z_closure_query_move(&callback3), &queryable_options)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), true); z_queryable_drop(z_queryable_move(&queryable2)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), true); z_queryable_drop(z_queryable_move(&queryable3)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), !complete); z_queryable_drop(z_queryable_move(&queryable1)); z_sleep_s(1); _check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), false); z_querier_drop(z_querier_move(&querier)); z_queryable_drop(z_queryable_move(&queryable_wrong)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #if Z_FEATURE_LOCAL_QUERYABLE == 1 static void test_matching_status_querier_locality(z_locality_t locality, bool create_local_queryable, bool create_remote_queryable, bool expected) { printf("test_matching_status_querier_locality locality=%d local=%d remote=%d expected=%d\n", (int)locality, create_local_queryable, create_remote_queryable, expected); z_owned_session_t s1, s2; z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); z_view_keyexpr_t k_queryable, k_querier; z_view_keyexpr_from_str(&k_queryable, QUERYABLE_EXPR); z_view_keyexpr_from_str(&k_querier, QUERIER_EXPR); assert_ok(z_open(&s1, z_config_move(&c1), NULL)); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); z_querier_options_t querier_opts; z_querier_options_default(&querier_opts); querier_opts.allowed_destination = locality; z_owned_querier_t querier; assert_ok(z_declare_querier(z_session_loan(&s1), &querier, z_view_keyexpr_loan(&k_querier), &querier_opts)); z_owned_queryable_t local_queryable; z_owned_queryable_t remote_queryable; if (create_local_queryable) { z_queryable_options_t opts; z_queryable_options_default(&opts); opts.allowed_origin = Z_LOCALITY_SESSION_LOCAL; z_owned_closure_query_t cb; z_closure_query(&cb, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s1), &local_queryable, z_view_keyexpr_loan(&k_queryable), z_closure_query_move(&cb), &opts)); } if (create_remote_queryable) { z_queryable_options_t opts; z_queryable_options_default(&opts); z_owned_closure_query_t cb; z_closure_query(&cb, NULL, NULL, NULL); assert_ok(z_declare_queryable(z_session_loan(&s2), &remote_queryable, z_view_keyexpr_loan(&k_queryable), z_closure_query_move(&cb), &opts)); } assert(_check_querier_status(&querier, z_loan_mut(s1), z_loan_mut(s2), expected)); if (create_local_queryable) { z_queryable_drop(z_queryable_move(&local_queryable)); } if (create_remote_queryable) { z_queryable_drop(z_queryable_move(&remote_queryable)); } z_querier_drop(z_querier_move(&querier)); z_session_drop(z_session_move(&s1)); z_session_drop(z_session_move(&s2)); } #endif // Z_FEATURE_LOCAL_QUERYABLE == 1 int main(int argc, char **argv) { (void)argc; (void)argv; test_matching_listener_publisher(true, false); test_matching_listener_publisher(false, false); test_matching_listener_publisher(true, true); test_matching_listener_publisher(false, true); test_matching_status_publisher(); test_matching_listener_querier(false, false); test_matching_listener_querier(true, false); test_matching_listener_querier(true, true); test_matching_listener_querier(false, true); test_matching_status_querier(false); test_matching_status_querier(true); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 test_matching_status_publisher_locality(Z_LOCALITY_SESSION_LOCAL, true, false, true); test_matching_status_publisher_locality(Z_LOCALITY_SESSION_LOCAL, false, true, false); test_matching_status_publisher_locality(Z_LOCALITY_SESSION_LOCAL, true, true, true); test_matching_status_publisher_locality(Z_LOCALITY_REMOTE, true, false, false); test_matching_status_publisher_locality(Z_LOCALITY_REMOTE, false, true, true); test_matching_status_publisher_locality(Z_LOCALITY_REMOTE, true, true, true); #endif #if Z_FEATURE_LOCAL_QUERYABLE == 1 test_matching_status_querier_locality(Z_LOCALITY_SESSION_LOCAL, true, false, true); test_matching_status_querier_locality(Z_LOCALITY_SESSION_LOCAL, false, true, false); test_matching_status_querier_locality(Z_LOCALITY_SESSION_LOCAL, true, true, true); test_matching_status_querier_locality(Z_LOCALITY_REMOTE, true, false, false); test_matching_status_querier_locality(Z_LOCALITY_REMOTE, false, true, true); test_matching_status_querier_locality(Z_LOCALITY_REMOTE, true, true, true); #endif } #else int main(int argc, char **argv) { (void)argc; (void)argv; } #endif ================================================ FILE: tests/z_api_null_drop_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #undef NDEBUG #include #include "zenoh-pico.h" // fill v with invalid values // set v to null // check if v it is null // make sure that drop on null does not crash // make sure that double drop on null does not crash // fill v with invalid values again // // set v1 to null // move v to v1 // make sure that v is null now #define TEST(name) \ { \ name v; \ memset(&v, -1, sizeof(v)); \ z_internal_null(&v); \ assert(!z_internal_check(v)); \ z_drop(z_move(v)); \ z_drop(z_move(v)); \ name v1; \ z_internal_null(&v1); \ memset(&v, -1, sizeof(v)); \ z_take(&v1, z_move(v)); \ assert(!z_internal_check(v)); \ } int main(void) { TEST(z_owned_session_t) TEST(z_owned_keyexpr_t) TEST(z_owned_config_t) TEST(z_owned_hello_t) TEST(z_owned_closure_sample_t) TEST(z_owned_closure_query_t) TEST(z_owned_closure_reply_t) TEST(z_owned_closure_hello_t) TEST(z_owned_closure_zid_t) TEST(z_owned_string_t) TEST(z_owned_string_array_t) TEST(z_owned_sample_t) TEST(z_owned_slice_t) TEST(z_owned_bytes_t) TEST(z_owned_bytes_writer_t) TEST(ze_owned_serializer_t) TEST(z_owned_encoding_t) #if Z_FEATURE_PUBLICATION == 1 TEST(z_owned_publisher_t) #endif #if Z_FEATURE_SUBSCRIPTION == 1 TEST(z_owned_subscriber_t) #endif #if Z_FEATURE_QUERYABLE == 1 TEST(z_owned_query_t) TEST(z_owned_queryable_t) #endif #if Z_FEATURE_QUERY == 1 TEST(z_owned_reply_t) #endif // Double drop not supported for these types // TEST(z_owned_task_t) // TEST(z_owned_mutex_t) // TEST(z_owned_condvar_t) return 0; } ================================================ FILE: tests/z_api_queryable_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/macros.h" #undef NDEBUG #include #if Z_FEATURE_QUERYABLE void test_queryable_keyexpr(void) { printf("Testing queryable key expressions...\n"); z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, "client"); z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } z_view_keyexpr_t ke1; z_view_keyexpr_from_str(&ke1, "test/queryable_keyexpr"); z_owned_queryable_t q1; z_owned_closure_query_t cb1; z_owned_fifo_handler_query_t h1; z_fifo_channel_query_new(&cb1, &h1, 16); z_declare_queryable(z_loan(s), &q1, z_loan(ke1), z_move(cb1), NULL); z_view_string_t sv1; z_keyexpr_as_view_string(z_queryable_keyexpr(z_loan(q1)), &sv1); assert(strncmp("test/queryable_keyexpr", z_string_data(z_loan(sv1)), z_string_len(z_loan(sv1))) == 0); z_drop(z_move(q1)); z_drop(z_move(h1)); z_view_keyexpr_t ke2; z_owned_keyexpr_t ke2_declared; z_view_keyexpr_from_str(&ke2, "test/queryable_declared_keyexpr"); z_declare_keyexpr(z_loan(s), &ke2_declared, z_loan(ke2)); z_owned_queryable_t q2; z_owned_closure_query_t cb2; z_owned_fifo_handler_query_t h2; z_fifo_channel_query_new(&cb2, &h2, 16); z_declare_queryable(z_loan(s), &q2, z_loan(ke2_declared), z_move(cb2), NULL); z_view_string_t sv2; z_keyexpr_as_view_string(z_queryable_keyexpr(z_loan(q2)), &sv2); assert(strncmp("test/queryable_declared_keyexpr", z_string_data(z_loan(sv2)), z_string_len(z_loan(sv2))) == 0); z_drop(z_move(ke2_declared)); z_drop(z_move(q2)); z_drop(z_move(h2)); z_drop(z_move(s)); } void open_sessions(z_owned_session_t *s, z_owned_session_t *s2, bool local) { z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, "client"); if (z_open(s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } if (local) { *s2 = *s; } else { z_owned_config_t config2; z_config_default(&config2); zp_config_insert(z_loan_mut(config2), Z_CONFIG_MODE_KEY, "client"); if (z_open(s2, z_move(config2), NULL) < 0) { printf("Unable to open second session for local queryable test!\n"); z_drop(z_move(*s)); exit(-1); } } } void test_get_accept_replies(bool local) { printf("Testing get accept_replies options local = %d...\n", local); z_owned_session_t s, s2; open_sessions(&s, &s2, local); // Declare a queryable on a wildcard key expression z_view_keyexpr_t queryable_ke; z_view_keyexpr_from_str(&queryable_ke, "test/accept_replies/**"); z_owned_queryable_t q; z_owned_closure_query_t cb; z_owned_fifo_handler_query_t h; z_fifo_channel_query_new(&cb, &h, 16); z_declare_queryable(z_loan(s), &q, z_loan(queryable_ke), z_move(cb), NULL); assert(z_internal_check(q)); z_sleep_s(1); // --- Test 1: Z_REPLY_KEYEXPR_ANY --- // With accept_replies = Z_REPLY_KEYEXPR_ANY, the queryable should be able // to reply on any key expression, even one not matching the query key { z_owned_fifo_handler_reply_t reply_handler; z_owned_closure_reply_t reply_cb; z_fifo_channel_reply_new(&reply_cb, &reply_handler, 16); z_view_keyexpr_t get_ke; z_view_keyexpr_from_str(&get_ke, "test/accept_replies/a"); z_get_options_t get_opts; z_get_options_default(&get_opts); get_opts.accept_replies = Z_REPLY_KEYEXPR_ANY; z_get(z_loan(s2), z_loan(get_ke), "", z_move(reply_cb), &get_opts); z_sleep_s(1); // Receive the incoming query z_owned_query_t query; assert(z_recv(z_loan(h), &query) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query)) == Z_REPLY_KEYEXPR_ANY); // Reply on a different key expression (not intersecting with query key) z_view_keyexpr_t reply_ke; z_view_keyexpr_from_str(&reply_ke, "test/accept_replies/b"); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "reply_any"); z_query_reply_options_t reply_opts; z_query_reply_options_default(&reply_opts); z_result_t ret = z_query_reply(z_loan(query), z_loan(reply_ke), z_move(payload), &reply_opts); assert(ret == _Z_RES_OK); z_drop(z_move(query)); z_sleep_s(1); // The reply should arrive and carry the replied key expression z_owned_reply_t reply; assert(z_recv(z_loan(reply_handler), &reply) == _Z_RES_OK); assert(z_reply_is_ok(z_loan(reply))); z_view_string_t reply_key_str; z_keyexpr_as_view_string(z_sample_keyexpr(z_reply_ok(z_loan(reply))), &reply_key_str); assert(strncmp("test/accept_replies/b", z_string_data(z_loan(reply_key_str)), z_string_len(z_loan(reply_key_str))) == 0); z_drop(z_move(reply)); z_drop(z_move(reply_handler)); } // --- Test 2: Z_REPLY_KEYEXPR_MATCHING_QUERY (default) --- // With accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY, the queryable must // reply on a key expression that intersects with the query key expression. // A reply on a matching key should be accepted; one on a non-matching key should be rejected. { // 2a: reply on a key expression matching the query key — should succeed z_owned_fifo_handler_reply_t reply_handler; z_owned_closure_reply_t reply_cb; z_fifo_channel_reply_new(&reply_cb, &reply_handler, 16); z_view_keyexpr_t get_ke; z_view_keyexpr_from_str(&get_ke, "test/accept_replies/c"); z_get_options_t get_opts; z_get_options_default(&get_opts); get_opts.accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY; z_get(z_loan(s2), z_loan(get_ke), "", z_move(reply_cb), &get_opts); z_sleep_s(1); z_owned_query_t query; assert(z_recv(z_loan(h), &query) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query)) == Z_REPLY_KEYEXPR_MATCHING_QUERY); z_view_keyexpr_t reply_ke_matching; z_view_keyexpr_from_str(&reply_ke_matching, "test/accept_replies/c"); z_owned_bytes_t payload_matching; z_bytes_from_static_str(&payload_matching, "reply_matching"); z_query_reply_options_t reply_opts; z_query_reply_options_default(&reply_opts); z_result_t ret = z_query_reply(z_loan(query), z_loan(reply_ke_matching), z_move(payload_matching), &reply_opts); assert(ret == _Z_RES_OK); z_drop(z_move(query)); z_sleep_s(1); z_owned_reply_t reply; assert(z_recv(z_loan(reply_handler), &reply) == _Z_RES_OK); assert(z_reply_is_ok(z_loan(reply))); z_view_string_t reply_key_str; z_keyexpr_as_view_string(z_sample_keyexpr(z_reply_ok(z_loan(reply))), &reply_key_str); assert(strncmp("test/accept_replies/c", z_string_data(z_loan(reply_key_str)), z_string_len(z_loan(reply_key_str))) == 0); z_drop(z_move(reply)); z_drop(z_move(reply_handler)); // 2b: reply on a key expression NOT matching the query key — should be rejected z_owned_fifo_handler_reply_t reply_handler2; z_owned_closure_reply_t reply_cb2; z_fifo_channel_reply_new(&reply_cb2, &reply_handler2, 16); z_view_keyexpr_t get_ke2; z_view_keyexpr_from_str(&get_ke2, "test/accept_replies/d"); z_get_options_t get_opts2; z_get_options_default(&get_opts2); get_opts2.accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY; z_get(z_loan(s2), z_loan(get_ke2), "", z_move(reply_cb2), &get_opts2); z_sleep_s(1); z_owned_query_t query2; assert(z_recv(z_loan(h), &query2) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query2)) == Z_REPLY_KEYEXPR_MATCHING_QUERY); z_view_keyexpr_t reply_ke_nonmatching; z_view_keyexpr_from_str(&reply_ke_nonmatching, "test/other/key"); z_owned_bytes_t payload_nonmatching; z_bytes_from_static_str(&payload_nonmatching, "reply_nonmatching"); ret = z_query_reply(z_loan(query2), z_loan(reply_ke_nonmatching), z_move(payload_nonmatching), &reply_opts); assert(ret != _Z_RES_OK); z_drop(z_move(query2)); z_drop(z_move(reply_handler2)); } z_drop(z_move(q)); z_drop(z_move(h)); z_drop(z_move(s)); if (!local) { z_drop(z_move(s2)); } } void test_querier_accept_replies(bool local) { printf("Testing querier accept_replies options local = %d...\n", local); z_owned_session_t s, s2; open_sessions(&s, &s2, local); // Declare a queryable on a wildcard key expression to handle incoming queries z_view_keyexpr_t queryable_ke; z_view_keyexpr_from_str(&queryable_ke, "test/querier_accept/**"); z_owned_queryable_t q; z_owned_closure_query_t cb; z_owned_fifo_handler_query_t h; z_fifo_channel_query_new(&cb, &h, 16); z_declare_queryable(z_loan(s), &q, z_loan(queryable_ke), z_move(cb), NULL); assert(z_internal_check(q)); z_sleep_s(1); // --- Test 1: Z_REPLY_KEYEXPR_ANY --- // With accept_replies = Z_REPLY_KEYEXPR_ANY set on the querier, the queryable // should be able to reply on any key expression, even one not matching the query key { z_view_keyexpr_t querier_ke; z_view_keyexpr_from_str(&querier_ke, "test/querier_accept/a"); z_querier_options_t querier_opts; z_querier_options_default(&querier_opts); querier_opts.accept_replies = Z_REPLY_KEYEXPR_ANY; z_owned_querier_t querier; z_declare_querier(z_loan(s2), &querier, z_loan(querier_ke), &querier_opts); assert(z_internal_check(querier)); z_sleep_s(1); z_owned_fifo_handler_reply_t reply_handler; z_owned_closure_reply_t reply_cb; z_fifo_channel_reply_new(&reply_cb, &reply_handler, 16); z_querier_get_options_t get_opts; z_querier_get_options_default(&get_opts); z_querier_get(z_loan(querier), "", z_move(reply_cb), &get_opts); z_sleep_s(1); // Receive the incoming query on the queryable side z_owned_query_t query; assert(z_recv(z_loan(h), &query) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query)) == Z_REPLY_KEYEXPR_ANY); // Reply on a different key expression (not intersecting with the query key) z_view_keyexpr_t reply_ke; z_view_keyexpr_from_str(&reply_ke, "test/querier_accept/b"); z_owned_bytes_t payload; z_bytes_from_static_str(&payload, "reply_any"); z_query_reply_options_t reply_opts; z_query_reply_options_default(&reply_opts); z_result_t ret = z_query_reply(z_loan(query), z_loan(reply_ke), z_move(payload), &reply_opts); assert(ret == _Z_RES_OK); z_drop(z_move(query)); z_sleep_s(1); // The reply should arrive and carry the replied key expression z_owned_reply_t reply; assert(z_recv(z_loan(reply_handler), &reply) == _Z_RES_OK); assert(z_reply_is_ok(z_loan(reply))); z_view_string_t reply_key_str; z_keyexpr_as_view_string(z_sample_keyexpr(z_reply_ok(z_loan(reply))), &reply_key_str); assert(strncmp("test/querier_accept/b", z_string_data(z_loan(reply_key_str)), z_string_len(z_loan(reply_key_str))) == 0); z_drop(z_move(reply)); z_drop(z_move(reply_handler)); z_drop(z_move(querier)); } // --- Test 2: Z_REPLY_KEYEXPR_MATCHING_QUERY (default) --- // With accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY set on the querier, // the queryable must reply on a key expression that intersects with the query key. // A reply on a matching key should be accepted; one on a non-matching key should be rejected. { // 2a: reply on a key expression matching the querier key — should succeed z_view_keyexpr_t querier_ke; z_view_keyexpr_from_str(&querier_ke, "test/querier_accept/c"); z_querier_options_t querier_opts; z_querier_options_default(&querier_opts); querier_opts.accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY; z_owned_querier_t querier; z_declare_querier(z_loan(s2), &querier, z_loan(querier_ke), &querier_opts); assert(z_internal_check(querier)); z_sleep_s(1); z_owned_fifo_handler_reply_t reply_handler; z_owned_closure_reply_t reply_cb; z_fifo_channel_reply_new(&reply_cb, &reply_handler, 16); z_querier_get_options_t get_opts; z_querier_get_options_default(&get_opts); z_querier_get(z_loan(querier), "", z_move(reply_cb), &get_opts); z_sleep_s(1); z_owned_query_t query; assert(z_recv(z_loan(h), &query) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query)) == Z_REPLY_KEYEXPR_MATCHING_QUERY); // Reply on a key matching the querier key expression z_view_keyexpr_t reply_ke_matching; z_view_keyexpr_from_str(&reply_ke_matching, "test/querier_accept/c"); z_owned_bytes_t payload_matching; z_bytes_from_static_str(&payload_matching, "reply_matching"); z_query_reply_options_t reply_opts; z_query_reply_options_default(&reply_opts); z_result_t ret = z_query_reply(z_loan(query), z_loan(reply_ke_matching), z_move(payload_matching), &reply_opts); assert(ret == _Z_RES_OK); z_drop(z_move(query)); z_sleep_s(1); z_owned_reply_t reply; assert(z_recv(z_loan(reply_handler), &reply) == _Z_RES_OK); assert(z_reply_is_ok(z_loan(reply))); z_view_string_t reply_key_str; z_keyexpr_as_view_string(z_sample_keyexpr(z_reply_ok(z_loan(reply))), &reply_key_str); assert(strncmp("test/querier_accept/c", z_string_data(z_loan(reply_key_str)), z_string_len(z_loan(reply_key_str))) == 0); z_drop(z_move(reply)); z_drop(z_move(reply_handler)); z_drop(z_move(querier)); // 2b: reply on a key expression NOT matching the querier key — should be rejected z_view_keyexpr_t querier_ke2; z_view_keyexpr_from_str(&querier_ke2, "test/querier_accept/d"); z_querier_options_t querier_opts2; z_querier_options_default(&querier_opts2); querier_opts2.accept_replies = Z_REPLY_KEYEXPR_MATCHING_QUERY; z_owned_querier_t querier2; z_declare_querier(z_loan(s2), &querier2, z_loan(querier_ke2), &querier_opts2); assert(z_internal_check(querier2)); z_sleep_s(1); z_owned_fifo_handler_reply_t reply_handler2; z_owned_closure_reply_t reply_cb2; z_fifo_channel_reply_new(&reply_cb2, &reply_handler2, 16); z_querier_get_options_t get_opts2; z_querier_get_options_default(&get_opts2); z_querier_get(z_loan(querier2), "", z_move(reply_cb2), &get_opts2); z_sleep_s(1); z_owned_query_t query2; assert(z_recv(z_loan(h), &query2) == _Z_RES_OK); assert(z_query_accepts_replies(z_loan(query2)) == Z_REPLY_KEYEXPR_MATCHING_QUERY); // Reply on a key expression not intersecting with the querier key z_view_keyexpr_t reply_ke_nonmatching; z_view_keyexpr_from_str(&reply_ke_nonmatching, "test/other/key"); z_owned_bytes_t payload_nonmatching; z_bytes_from_static_str(&payload_nonmatching, "reply_nonmatching"); ret = z_query_reply(z_loan(query2), z_loan(reply_ke_nonmatching), z_move(payload_nonmatching), &reply_opts); assert(ret != _Z_RES_OK); z_drop(z_move(query2)); z_drop(z_move(reply_handler2)); z_drop(z_move(querier2)); } z_drop(z_move(q)); z_drop(z_move(h)); z_drop(z_move(s)); if (!local) { z_drop(z_move(s2)); } } int main(void) { test_queryable_keyexpr(); test_get_accept_replies(false); test_querier_accept_replies(false); #if Z_FEATURE_LOCAL_QUERYABLE == 1 test_get_accept_replies(true); test_querier_accept_replies(true); #endif return 0; } #else int main(void) {} #endif ================================================ FILE: tests/z_api_source_info_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/bytes.h" #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_QUERY == 1 && Z_FEATURE_QUERYABLE == 1 && \ defined(Z_FEATURE_UNSTABLE_API) #undef NDEBUG #include const char *keyexpr = "zenoh-pico/source_info/test"; const char test_payload[] = "source-info-test"; #define SAMPLE_BUF_CAP 128 #define assert_ok(x) \ { \ int ret = (int)x; \ if (ret != Z_OK) { \ printf("%s failed: %d\n", #x, ret); \ assert(false); \ } \ } typedef struct sample_capture_t { atomic_bool ready; bool payload_ok; z_entity_global_id_t source; uint32_t sn; z_sample_kind_t kind; } sample_capture_t; static void sample_capture_reset(sample_capture_t *capture) { atomic_store_explicit(&capture->ready, false, memory_order_relaxed); capture->payload_ok = false; capture->source = (z_entity_global_id_t){0}; capture->sn = 0; capture->kind = Z_SAMPLE_KIND_PUT; } static bool wait_for_capture(sample_capture_t *capture) { for (int attempt = 0; attempt < 50; ++attempt) { if (atomic_load_explicit(&capture->ready, memory_order_acquire)) { return true; } z_sleep_ms(100); } return false; } static void sample_capture_callback(_z_sample_t *sample, void *arg) { sample_capture_t *capture = (sample_capture_t *)arg; if ((sample == NULL) || (capture == NULL)) { return; } capture->kind = sample->kind; if (sample->kind == Z_SAMPLE_KIND_PUT) { size_t len = _z_bytes_len(&sample->payload); if ((len == sizeof(test_payload) - 1) && (len < SAMPLE_BUF_CAP)) { char buf[SAMPLE_BUF_CAP]; size_t copied = _z_bytes_to_buf(&sample->payload, (uint8_t *)buf, len); capture->payload_ok = (copied == len) && (memcmp(buf, test_payload, sizeof(test_payload) - 1) == 0); } else { capture->payload_ok = false; } } else { capture->payload_ok = true; } capture->source = sample->source_info._source_id; capture->sn = sample->source_info._source_sn; atomic_store_explicit(&capture->ready, true, memory_order_release); } static void assert_source_info_equal(const z_entity_global_id_t *expected, uint32_t expected_sn, const z_entity_global_id_t *actual, uint32_t actual_sn) { assert(expected->eid == actual->eid); assert(memcmp(expected->zid.id, actual->zid.id, Z_ZID_LENGTH) == 0); assert(expected_sn == actual_sn); } void test_source_info(bool put, bool publisher, bool local_subscriber) { z_owned_config_t c1; z_config_default(&c1); z_owned_session_t s1; assert_ok(z_open(&s1, z_config_move(&c1), NULL)); z_owned_session_t s2; const z_loaned_session_t *sub_session = z_loan(s1); if (!local_subscriber) { z_owned_config_t c2; z_config_default(&c2); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); sub_session = z_loan(s2); } z_view_keyexpr_t ke; assert_ok(z_view_keyexpr_from_str(&ke, keyexpr)); z_owned_closure_sample_t callback; z_owned_fifo_handler_sample_t handler; sample_capture_t capture; if (local_subscriber) { sample_capture_reset(&capture); assert_ok(z_closure_sample(&callback, sample_capture_callback, NULL, &capture)); } else { assert_ok(z_fifo_channel_sample_new(&callback, &handler, 1)); } z_owned_subscriber_t sub; assert_ok(z_declare_subscriber(sub_session, &sub, z_loan(ke), z_move(callback), NULL)); z_sleep_s(1); z_owned_bytes_t payload; if (put) { assert_ok(z_bytes_copy_from_str(&payload, test_payload)); } z_entity_global_id_t egid = _z_entity_global_id_null(); uint32_t sn = z_random_u32(); if (put && publisher) { z_owned_publisher_t pub; assert_ok(z_declare_publisher(z_loan(s1), &pub, z_loan(ke), NULL)); z_sleep_s(1); z_publisher_put_options_t opt; z_publisher_put_options_default(&opt); egid = z_publisher_id(z_loan(pub)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; z_publisher_put(z_loan(pub), z_move(payload), &opt); z_sleep_s(1); assert_ok(z_undeclare_publisher(z_move(pub))); } else if (put) { z_put_options_t opt; z_put_options_default(&opt); egid = z_session_id(z_loan(s1)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; assert_ok(z_put(z_loan(s1), z_loan(ke), z_move(payload), &opt)); } else if (!put && publisher) { z_owned_publisher_t pub; assert_ok(z_declare_publisher(z_loan(s1), &pub, z_loan(ke), NULL)); z_sleep_s(1); z_publisher_delete_options_t opt; z_publisher_delete_options_default(&opt); egid = z_publisher_id(z_loan(pub)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; z_publisher_delete(z_loan(pub), &opt); z_sleep_s(1); assert_ok(z_undeclare_publisher(z_move(pub))); } else { z_delete_options_t opt; z_delete_options_default(&opt); egid = z_session_id(z_loan(s1)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; assert_ok(z_delete(z_loan(s1), z_loan(ke), &opt)); } z_sleep_s(1); if (local_subscriber) { assert(wait_for_capture(&capture)); assert(capture.kind == (put ? Z_SAMPLE_KIND_PUT : Z_SAMPLE_KIND_DELETE)); if (put) { assert(capture.payload_ok); } assert_source_info_equal(&egid, sn, &capture.source, capture.sn); } else { z_owned_sample_t sample; z_result_t r = Z_CHANNEL_NODATA; for (int attempt = 0; attempt < 50; ++attempt) { r = z_fifo_handler_sample_try_recv(z_fifo_handler_sample_loan(&handler), &sample); if (r == Z_OK) { break; } assert(r == Z_CHANNEL_NODATA); z_sleep_ms(100); } assert(r == Z_OK); if (put) { z_owned_string_t value; assert_ok(z_bytes_to_string(z_sample_payload(z_loan(sample)), &value)); assert((sizeof(test_payload) - 1) == z_string_len(z_loan(value))); assert(memcmp(test_payload, z_string_data(z_loan(value)), sizeof(test_payload) - 1) == 0); z_drop(z_move(value)); } const z_source_info_t *si = z_sample_source_info(z_loan(sample)); assert(si != NULL); z_entity_global_id_t gid = z_source_info_id(si); assert_source_info_equal(&egid, sn, &gid, z_source_info_sn(si)); z_drop(z_move(sample)); } assert_ok(z_undeclare_subscriber(z_move(sub))); if (!local_subscriber) { z_drop(z_move(handler)); } z_session_drop(z_session_move(&s1)); if (!local_subscriber) { z_session_drop(z_session_move(&s2)); } } void test_source_info_put(bool publisher, bool local_subscriber) { printf("test_source_info_put: publisher=%d, local_subscriber=%d\n", publisher, local_subscriber); test_source_info(true, publisher, local_subscriber); } void test_source_info_delete(bool publisher, bool local_subscriber) { printf("test_source_info_delete: publisher=%d, local_subscriber=%d\n", publisher, local_subscriber); test_source_info(false, publisher, local_subscriber); } void test_source_info_query(bool use_querier, bool local_queryable) { printf("test_source_info_query: querier=%d, local_queryable=%d\n", use_querier, local_queryable); z_owned_config_t c1; z_config_default(&c1); z_owned_session_t s1; assert_ok(z_open(&s1, z_config_move(&c1), NULL)); z_owned_session_t s2 = s1; if (!local_queryable) { z_owned_config_t c2; z_config_default(&c2); assert_ok(z_open(&s2, z_config_move(&c2), NULL)); } z_view_keyexpr_t ke; assert_ok(z_view_keyexpr_from_str(&ke, keyexpr)); z_owned_closure_query_t query_callback; z_owned_closure_reply_t reply_callback; z_owned_fifo_handler_query_t query_handler; z_owned_fifo_handler_reply_t reply_handler; z_fifo_channel_query_new(&query_callback, &query_handler, 1); z_fifo_channel_reply_new(&reply_callback, &reply_handler, 1); z_owned_queryable_t qbl; assert_ok(z_declare_queryable(z_loan(s2), &qbl, z_loan(ke), z_move(query_callback), NULL)); z_sleep_s(1); z_entity_global_id_t egid = _z_entity_global_id_null(); uint32_t sn = z_random_u32(); z_owned_querier_t querier; z_internal_querier_null(&querier); if (use_querier) { assert_ok(z_declare_querier(z_loan(s1), &querier, z_loan(ke), NULL)); z_sleep_s(1); z_querier_get_options_t opt; z_querier_get_options_default(&opt); egid = z_querier_id(z_loan(querier)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; z_querier_get(z_loan(querier), NULL, z_move(reply_callback), &opt); } else { z_get_options_t opt; z_get_options_default(&opt); egid = z_session_id(z_loan(s1)); z_source_info_t si = z_source_info_new(&egid, sn); opt.source_info = &si; z_get(z_loan(s1), z_loan(ke), NULL, z_move(reply_callback), &opt); } z_sleep_s(1); z_owned_query_t q; assert(z_try_recv(z_loan(query_handler), &q) == Z_OK); const z_source_info_t *si = z_query_source_info(z_loan(q)); assert(si != NULL); assert_source_info_equal(&egid, sn, &si->_source_id, si->_source_sn); z_query_reply_options_t opts; z_query_reply_options_default(&opts); egid = z_queryable_id(z_loan(qbl)); sn = z_random_u32(); z_source_info_t si2 = z_source_info_new(&egid, sn); opts.source_info = &si2; z_query_reply(z_loan(q), z_loan(ke), NULL, &opts); z_query_drop(z_move(q)); z_sleep_s(1); z_owned_reply_t r; assert(z_try_recv(z_loan(reply_handler), &r) == Z_OK); assert(z_reply_is_ok(z_loan(r))); si = z_sample_source_info(z_reply_ok(z_loan(r))); assert(si != NULL); assert_source_info_equal(&egid, sn, &si->_source_id, si->_source_sn); z_reply_drop(z_move(r)); z_drop(z_move(qbl)); z_drop(z_move(query_handler)); z_drop(z_move(reply_handler)); z_drop(z_move(querier)); z_drop(z_move(s1)); if (!local_queryable) { z_drop(z_move(s2)); } } int main(int argc, char **argv) { (void)argc; (void)argv; test_source_info_put(false, false); test_source_info_delete(false, false); test_source_info_put(true, false); test_source_info_delete(true, false); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 test_source_info_put(false, true); test_source_info_put(true, true); #endif test_source_info_query(false, false); test_source_info_query(true, false); #if Z_FEATURE_LOCAL_QUERYABLE == 1 test_source_info_query(false, true); test_source_info_query(true, true); #endif } #else int main(int argc, char **argv) { (void)argc; (void)argv; } #endif ================================================ FILE: tests/z_background_executor_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico/runtime/background_executor.h" #include "zenoh-pico/system/platform.h" #undef NDEBUG #include #if Z_FEATURE_MULTI_THREAD == 1 // ─── Helpers ──────────────────────────────────────────────────────────────── // Shared state between the test thread and the background executor thread. // All fields are protected by mutex + condvar except where noted. typedef struct { _z_mutex_t mutex; _z_condvar_t condvar; int call_count; // number of times fn body was entered bool destroyed; // destroy_fn was called unsigned long wait_ms; // wait for timed part of fn_reschedule_once } test_arg_t; static void test_arg_init(test_arg_t *a) { _z_mutex_init(&a->mutex); _z_condvar_init(&a->condvar); a->call_count = 0; a->destroyed = false; a->wait_ms = 0; } static void test_arg_clear(test_arg_t *a) { _z_condvar_drop(&a->condvar); _z_mutex_drop(&a->mutex); } // Block until call_count >= expected. static void test_arg_wait_calls(test_arg_t *a, int expected) { _z_mutex_lock(&a->mutex); while (a->call_count < expected) { _z_condvar_wait(&a->condvar, &a->mutex); } _z_mutex_unlock(&a->mutex); } static int test_arg_get_calls(test_arg_t *a) { _z_mutex_lock(&a->mutex); int calls = a->call_count; _z_mutex_unlock(&a->mutex); return calls; } static int test_arg_get_destroyed(test_arg_t *a) { _z_mutex_lock(&a->mutex); bool destroyed = a->destroyed; _z_mutex_unlock(&a->mutex); return destroyed; } // Block until destroyed == true. static void test_arg_wait_destroyed(test_arg_t *a) { _z_mutex_lock(&a->mutex); while (!a->destroyed) { _z_condvar_wait(&a->condvar, &a->mutex); } _z_mutex_unlock(&a->mutex); } // ── fut_fn helpers ──────────────────────────────────────────────────────────── // Increments call_count and finishes immediately. static _z_fut_fn_result_t fn_finish(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; _z_mutex_lock(&a->mutex); a->call_count++; _z_condvar_signal_all(&a->condvar); _z_mutex_unlock(&a->mutex); return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } // Reschedules with immediate wake_up_time on first call, finishes on second. static _z_fut_fn_result_t fn_reschedule_once(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; _z_mutex_lock(&a->mutex); int count = ++a->call_count; _z_condvar_signal_all(&a->condvar); _z_mutex_unlock(&a->mutex); z_clock_t wake = z_clock_now(); z_clock_advance_ms(&wake, a->wait_ms); if (count == 1) { return (_z_fut_fn_result_t){ ._status = _Z_FUT_STATUS_SLEEPING, ._wake_up_time = wake, }; } return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } static void destroy_fn(void *arg) { test_arg_t *a = (test_arg_t *)arg; _z_mutex_lock(&a->mutex); a->destroyed = true; _z_condvar_signal_all(&a->condvar); _z_mutex_unlock(&a->mutex); } // ─── Tests ─────────────────────────────────────────────────────────────────── // _z_background_executor_init + _z_background_executor_destroy with no tasks. static void test_new_destroy_no_tasks(void) { printf("Test: new and destroy with no tasks\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); _z_background_executor_destroy(&be); } // A spawned future runs on the background thread; destroy_fn is called. static void test_spawn_runs_task(void) { printf("Test: spawn runs task on background thread and calls destroy_fn\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); test_arg_wait_calls(&arg, 1); assert(arg.call_count == 1); test_arg_wait_destroyed(&arg); assert(arg.destroyed == true); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // A future with a handle: cancel before it runs — body never called, destroy_fn called. static void test_cancel_before_execution(void) { printf("Test: cancel before execution prevents body; destroy_fn still called\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); // Suspend so the task cannot be picked up before we cancel it assert(_z_background_executor_suspend(&be) == _Z_RES_OK); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h; assert(_z_background_executor_spawn(&be, &fut, &h) == _Z_RES_OK); assert(!_z_fut_handle_is_null(h)); // Cancel while executor is suspended assert(_z_background_executor_cancel_fut(&be, &h) == _Z_RES_OK); _z_fut_status_t status; assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status == _Z_FUT_STATUS_READY); // cancelled tasks are considered ready (not pending, running, or sleeping); assert(_z_background_executor_resume(&be) == _Z_RES_OK); // destroy_fn must be called even though the task was cancelled test_arg_wait_destroyed(&arg); assert(arg.destroyed == true); assert(arg.call_count == 0); // body never ran _z_background_executor_destroy(&be); test_arg_clear(&arg); } // A future with timed reschedule runs exactly twice. static void test_timed_reschedule_runs_twice(void) { printf("Test: timed reschedule runs task body exactly twice\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); arg.wait_ms = 500; _z_fut_t fut = _z_fut_new(&arg, fn_reschedule_once, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); z_sleep_ms(100); // give the background thread a chance to run the first call and reschedule assert(test_arg_get_calls(&arg) == 1); // first call must have happened, but not the second yet assert(test_arg_get_destroyed(&arg) == false); // destroy_fn must not have been called yet z_sleep_ms(500); // wait for the rescheduled call to become ready and run assert(test_arg_get_calls(&arg) == 2); assert(test_arg_get_destroyed(&arg) == true); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Tasks do not run while the executor is suspended; they run after resume. static void test_suspend_blocks_execution(void) { printf("Test: tasks do not run while suspended; run after resume\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); assert(_z_background_executor_suspend(&be) == _Z_RES_OK); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); // Give the background thread ample opportunity to (incorrectly) run the task z_sleep_ms(100); _z_mutex_lock(&arg.mutex); int calls_while_suspended = arg.call_count; _z_mutex_unlock(&arg.mutex); assert(calls_while_suspended == 0); // must not have run yet assert(_z_background_executor_resume(&be) == _Z_RES_OK); test_arg_wait_calls(&arg, 1); assert(arg.call_count == 1); test_arg_wait_destroyed(&arg); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Nested suspend/resume: execution resumes only after all suspenders have resumed. static void test_nested_suspend_resume(void) { printf("Test: nested suspend/resume — task runs only after all resumes\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); // Two independent suspenders assert(_z_background_executor_suspend(&be) == _Z_RES_OK); assert(_z_background_executor_suspend(&be) == _Z_RES_OK); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); z_sleep_ms(100); _z_mutex_lock(&arg.mutex); assert(arg.call_count == 0); _z_mutex_unlock(&arg.mutex); // First resume — still one suspender outstanding assert(_z_background_executor_resume(&be) == _Z_RES_OK); z_sleep_ms(100); _z_mutex_lock(&arg.mutex); assert(arg.call_count == 0); _z_mutex_unlock(&arg.mutex); // Second resume — now fully unblocked assert(_z_background_executor_resume(&be) == _Z_RES_OK); test_arg_wait_calls(&arg, 1); assert(arg.call_count == 1); test_arg_wait_destroyed(&arg); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Multiple concurrent tasks all complete. static void test_multiple_tasks_all_complete(void) { printf("Test: multiple concurrent tasks all complete\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); const int N = 8; test_arg_t args[8]; z_clock_t start = z_clock_now(); for (int i = 0; i < N; i++) { test_arg_init(&args[i]); args[i].wait_ms = (unsigned long)(300 * (i + 1)); // stagger the wait times so tasks finish in order _z_fut_t fut = _z_fut_new(&args[i], fn_reschedule_once, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); } for (int i = 0; i < N; i++) { test_arg_wait_calls(&args[i], 2); test_arg_wait_destroyed(&args[i]); z_clock_t now = z_clock_now(); unsigned long elapsed_ms = zp_clock_elapsed_ms_since(&now, &start); assert(args[i].call_count == 2); assert(args[i].destroyed == true); assert(elapsed_ms >= args[i].wait_ms); // each task must have taken at least as long as its wait time, indicating it assert(elapsed_ms <= args[i].wait_ms + 300); // but not too much longer } _z_background_executor_destroy(&be); for (int i = 0; i < N; i++) { test_arg_clear(&args[i]); } } // destroy while tasks are pending: destroy_fn is called for each. static void test_destroy_with_pending_tasks(void) { printf("Test: destroy calls destroy_fn on all pending tasks\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); const int N = 4; test_arg_t args[4]; // Queue tasks while suspended so none run before destroy assert(_z_background_executor_suspend(&be) == _Z_RES_OK); for (int i = 0; i < N; i++) { test_arg_init(&args[i]); _z_fut_t fut = _z_fut_new(&args[i], fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); } // Resume so the background thread can process cancellations on destroy assert(_z_background_executor_resume(&be) == _Z_RES_OK); // Destroy immediately — some or all tasks may not have run yet _z_background_executor_destroy(&be); // Every destroy_fn must have been called by now (destroy is synchronous) for (int i = 0; i < N; i++) { assert(args[i].destroyed == true); test_arg_clear(&args[i]); } } // Handle status is PENDING before execution, READY after. static void test_handle_status_transitions(void) { printf("Test: handle status transitions PENDING -> READY\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); assert(_z_background_executor_suspend(&be) == _Z_RES_OK); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h; assert(_z_background_executor_spawn(&be, &fut, &h) == _Z_RES_OK); _z_fut_status_t status; assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status == _Z_FUT_STATUS_RUNNING); assert(_z_background_executor_resume(&be) == _Z_RES_OK); test_arg_wait_calls(&arg, 1); assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status == _Z_FUT_STATUS_READY); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Deferred init: create without thread, add tasks, then start. static void test_deferred_init_and_start(void) { printf("Test: deferred init allows adding tasks before start\n"); _z_background_executor_t be; assert(_z_background_executor_init_deferred(&be) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); // Spawn a task before the executor thread is running _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h; assert(_z_background_executor_spawn(&be, &fut, &h) == _Z_RES_OK); assert(!_z_fut_handle_is_null(h)); // Task should be pending but not executed _z_fut_status_t status; assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status == _Z_FUT_STATUS_RUNNING); z_sleep_ms(100); assert(test_arg_get_calls(&arg) == 0); // no thread running, so task cannot execute // Now start the background thread assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); // Task should now execute test_arg_wait_calls(&arg, 1); assert(arg.call_count == 1); test_arg_wait_destroyed(&arg); assert(arg.destroyed == true); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Deferred init: destroy without ever starting should clean up properly. static void test_deferred_destroy_without_start(void) { printf("Test: deferred init + destroy without start cleans up\n"); _z_background_executor_t be; assert(_z_background_executor_init_deferred(&be) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut, NULL) == _Z_RES_OK); // Destroy without starting — destroy_fn must still be called _z_background_executor_destroy(&be); assert(arg.destroyed == true); assert(arg.call_count == 0); // body never ran test_arg_clear(&arg); } // Deferred init: starting second time is no-op. static void test_deferred_second_start_is_no_op(void) { printf("Test: deferred init + double start is no-op\n"); _z_background_executor_t be; assert(_z_background_executor_init_deferred(&be) == _Z_RES_OK); assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); _z_background_executor_destroy(&be); } // Deferred init: cancel a task before starting, then start — cancelled task should not run. static void test_deferred_cancel_before_start(void) { printf("Test: deferred init + cancel before start\n"); _z_background_executor_t be; assert(_z_background_executor_init_deferred(&be) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h; assert(_z_background_executor_spawn(&be, &fut, &h) == _Z_RES_OK); // Cancel before starting assert(_z_background_executor_cancel_fut(&be, &h) == _Z_RES_OK); _z_fut_status_t status; assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status == _Z_FUT_STATUS_READY); // Start the executor assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); // Give it time, but the task should never run z_sleep_ms(100); assert(test_arg_get_calls(&arg) == 0); assert(test_arg_get_destroyed(&arg) == true); // destroy_fn must have been called on cancel _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Stop a running executor, verify tasks don't execute, then restart. static void test_stop_and_restart(void) { printf("Test: stop and restart executor\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); // Run a task to confirm executor is working test_arg_t arg1; test_arg_init(&arg1); _z_fut_t fut1 = _z_fut_new(&arg1, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut1, NULL) == _Z_RES_OK); test_arg_wait_calls(&arg1, 1); assert(test_arg_get_calls(&arg1) == 1); // Stop the executor assert(_z_background_executor_stop(&be) == _Z_RES_OK); // Stopping again should be a no-op (idempotent) assert(_z_background_executor_stop(&be) == _Z_RES_OK); // Spawn a task while stopped — should succeed but not execute test_arg_t arg2; test_arg_init(&arg2); _z_fut_t fut2 = _z_fut_new(&arg2, fn_finish, destroy_fn); _z_fut_handle_t h2; assert(_z_background_executor_spawn(&be, &fut2, &h2) == _Z_RES_OK); z_sleep_ms(100); assert(test_arg_get_calls(&arg2) == 0); // Restart the executor assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); // The pending task should now execute test_arg_wait_calls(&arg2, 1); assert(test_arg_get_calls(&arg2) == 1); _z_background_executor_destroy(&be); test_arg_clear(&arg1); test_arg_clear(&arg2); } // Stop executor with pending tasks — tasks should be preserved, not destroyed. static void test_stop_preserves_pending_tasks(void) { printf("Test: stop preserves pending tasks\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); // Suspend so the task doesn't run before we stop assert(_z_background_executor_suspend(&be) == _Z_RES_OK); test_arg_t arg; test_arg_init(&arg); arg.wait_ms = 500; // ensure the task would still be pending after we stop _z_fut_t fut = _z_fut_new(&arg, fn_reschedule_once, destroy_fn); assert(_z_background_executor_resume(&be) == _Z_RES_OK); _z_fut_handle_t h; assert(_z_background_executor_spawn(&be, &fut, &h) == _Z_RES_OK); // Stop the executor — task should still be pending, destroy_fn should NOT have been called assert(_z_background_executor_stop(&be) == _Z_RES_OK); z_sleep_ms(1000); _z_fut_status_t status; assert(_z_background_executor_get_fut_status(&be, &h, &status) == _Z_RES_OK); assert(status != _Z_FUT_STATUS_READY); assert(arg.destroyed == false); // Restart and let it complete assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); z_sleep_ms(1000); // give it time to run the first call and reschedule test_arg_wait_calls(&arg, 2); test_arg_wait_destroyed(&arg); _z_background_executor_destroy(&be); test_arg_clear(&arg); } // Suspend executor, than verify that stopping does not hang, nor causes pending tasks to be destroyed. // Then restart and verify pending tasks still run. static void test_suspend_stop_restart_resume(void) { printf("Test: suspend-stop-restart-resume executor\n"); _z_background_executor_t be; assert(_z_background_executor_init(&be, NULL) == _Z_RES_OK); // Suspend the executor so tasks won't run until we explicitly resume assert(_z_background_executor_suspend(&be) == _Z_RES_OK); // Run a task to confirm executor is working test_arg_t arg1; test_arg_init(&arg1); _z_fut_t fut1 = _z_fut_new(&arg1, fn_finish, destroy_fn); assert(_z_background_executor_spawn(&be, &fut1, NULL) == _Z_RES_OK); // Ensure that stopping suspended executor works assert(_z_background_executor_stop(&be) == _Z_RES_OK); z_sleep_ms(500); // give it time to (incorrectly) run if stop didn't work assert(test_arg_get_calls(&arg1) == 0); assert(test_arg_get_destroyed(&arg1) == false); // Restart the executor assert(_z_background_executor_start(&be, NULL) == _Z_RES_OK); // Resume the executor so the pending task can run assert(_z_background_executor_resume(&be) == _Z_RES_OK); z_sleep_ms(500); // give it time to (incorrectly) run if stop didn't work assert(test_arg_get_calls(&arg1) == 1); assert(test_arg_get_destroyed(&arg1) == true); _z_background_executor_destroy(&be); test_arg_clear(&arg1); } // ─── main ──────────────────────────────────────────────────────────────────── int main(void) { test_new_destroy_no_tasks(); test_spawn_runs_task(); test_cancel_before_execution(); test_timed_reschedule_runs_twice(); test_suspend_blocks_execution(); test_nested_suspend_resume(); test_multiple_tasks_all_complete(); test_destroy_with_pending_tasks(); test_handle_status_transitions(); test_deferred_init_and_start(); test_deferred_destroy_without_start(); test_deferred_second_start_is_no_op(); test_deferred_cancel_before_start(); test_stop_and_restart(); test_stop_preserves_pending_tasks(); test_suspend_stop_restart_resume(); printf("All background executor tests passed.\n"); return 0; } #else int main(void) { printf("Skipping background executor tests (Z_FEATURE_MULTI_THREAD disabled)\n"); return 0; } #endif ================================================ FILE: tests/z_bytes_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/collections/bytes.h" #undef NDEBUG #include void test_null_bytes(void) { _z_bytes_t b = _z_bytes_null(); assert(_z_bytes_len(&b) == 0); assert(_z_bytes_is_empty(&b)); assert(!_z_bytes_check(&b)); assert(_z_bytes_num_slices(&b) == 0); _z_bytes_drop(&b); // no crush } void test_slice(void) { uint8_t data[5] = {1, 2, 3, 4, 5}; uint8_t data_out[5] = {0}; _z_slice_t s = _z_slice_copy_from_buf(data, 5); _z_bytes_t b; _z_bytes_from_slice(&b, &s); assert(_z_bytes_len(&b) == 5); assert(!_z_bytes_is_empty(&b)); assert(_z_bytes_check(&b)); assert(_z_bytes_num_slices(&b) == 1); assert(_z_slice_eq(_z_slice_simple_rc_value(&_z_bytes_get_slice(&b, 0)->slice), &s)); assert(_z_bytes_to_buf(&b, data_out, 5) == 5); assert(memcmp(data, data_out, 5) == 0); _z_bytes_drop(&b); } void test_append(void) { uint8_t data1[5] = {1, 2, 3, 4, 5}; uint8_t data2[5] = {1, 2, 6, 7, 8}; uint8_t data3[3] = {3, 9, 10}; uint8_t data_in[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; uint8_t data_out[10] = {0}; _z_slice_t tmp1 = _z_slice_copy_from_buf(data1, 5); _z_slice_t tmp2 = _z_slice_copy_from_buf(data2, 5); _z_slice_t tmp3 = _z_slice_copy_from_buf(data3, 3); _z_arc_slice_t s1 = _z_arc_slice_wrap(&tmp1, 0, 5); _z_arc_slice_t s2 = _z_arc_slice_wrap(&tmp2, 2, 3); _z_arc_slice_t s3 = _z_arc_slice_wrap(&tmp3, 1, 2); _z_bytes_t b = _z_bytes_null(); _z_bytes_append_slice(&b, &s1); _z_bytes_append_slice(&b, &s2); _z_bytes_append_slice(&b, &s3); assert(_z_bytes_len(&b) == 10); assert(!_z_bytes_is_empty(&b)); assert(_z_bytes_check(&b)); assert(_z_bytes_num_slices(&b) == 3); assert( _z_slice_eq(_z_slice_simple_rc_value(&_z_bytes_get_slice(&b, 0)->slice), _z_slice_simple_rc_value(&s1.slice))); assert( _z_slice_eq(_z_slice_simple_rc_value(&_z_bytes_get_slice(&b, 1)->slice), _z_slice_simple_rc_value(&s2.slice))); assert( _z_slice_eq(_z_slice_simple_rc_value(&_z_bytes_get_slice(&b, 2)->slice), _z_slice_simple_rc_value(&s3.slice))); assert(_z_bytes_to_buf(&b, data_out, 15) == 10); assert(memcmp(data_in, data_out, 10) == 0); _z_bytes_drop(&b); } void test_reader_read(void) { uint8_t data1[5] = {1, 2, 3, 4, 5}; uint8_t data2[5] = {1, 2, 6, 7, 8}; uint8_t data3[3] = {3, 9, 10}; uint8_t data_in[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; uint8_t data_out[10] = {0}; _z_slice_t tmp1 = _z_slice_copy_from_buf(data1, 5); _z_slice_t tmp2 = _z_slice_copy_from_buf(data2, 5); _z_slice_t tmp3 = _z_slice_copy_from_buf(data3, 3); _z_arc_slice_t s1 = _z_arc_slice_wrap(&tmp1, 0, 5); _z_arc_slice_t s2 = _z_arc_slice_wrap(&tmp2, 2, 3); _z_arc_slice_t s3 = _z_arc_slice_wrap(&tmp3, 1, 2); _z_bytes_t b = _z_bytes_null(); _z_bytes_append_slice(&b, &s1); _z_bytes_append_slice(&b, &s2); _z_bytes_append_slice(&b, &s3); _z_bytes_reader_t reader = _z_bytes_get_reader(&b); uint8_t out; assert(_z_bytes_reader_tell(&reader) == 0); _z_bytes_reader_read(&reader, &out, 1); assert(_z_bytes_reader_tell(&reader) == 1); assert(out == 1); _z_bytes_reader_read(&reader, data_out, 3); assert(_z_bytes_reader_tell(&reader) == 4); assert(memcmp(data_out, data_in + 1, 3) == 0); _z_bytes_reader_read(&reader, data_out, 6); assert(_z_bytes_reader_tell(&reader) == 10); assert(memcmp(data_out, data_in + 4, 6) == 0); _z_bytes_drop(&b); } void test_reader_seek(void) { uint8_t data1[5] = {1, 2, 3, 4, 5}; uint8_t data2[5] = {1, 2, 6, 7, 8}; uint8_t data3[3] = {3, 9, 10}; _z_slice_t tmp1 = _z_slice_copy_from_buf(data1, 5); _z_slice_t tmp2 = _z_slice_copy_from_buf(data2, 5); _z_slice_t tmp3 = _z_slice_copy_from_buf(data3, 3); _z_arc_slice_t s1 = _z_arc_slice_wrap(&tmp1, 0, 5); _z_arc_slice_t s2 = _z_arc_slice_wrap(&tmp2, 2, 3); _z_arc_slice_t s3 = _z_arc_slice_wrap(&tmp3, 1, 2); _z_bytes_t b = _z_bytes_null(); _z_bytes_append_slice(&b, &s1); _z_bytes_append_slice(&b, &s2); _z_bytes_append_slice(&b, &s3); _z_bytes_reader_t reader = _z_bytes_get_reader(&b); assert(_z_bytes_reader_tell(&reader) == 0); assert(_z_bytes_reader_seek(&reader, 3, SEEK_CUR) == 0); assert(_z_bytes_reader_tell(&reader) == 3); assert(_z_bytes_reader_seek(&reader, 5, SEEK_CUR) == 0); assert(_z_bytes_reader_tell(&reader) == 8); assert(_z_bytes_reader_seek(&reader, 10, SEEK_CUR) != 0); assert(_z_bytes_reader_seek(&reader, 0, SEEK_SET) == 0); assert(_z_bytes_reader_tell(&reader) == 0); assert(_z_bytes_reader_seek(&reader, 3, SEEK_SET) == 0); assert(_z_bytes_reader_tell(&reader) == 3); assert(_z_bytes_reader_seek(&reader, 8, SEEK_SET) == 0); assert(_z_bytes_reader_tell(&reader) == 8); assert(_z_bytes_reader_seek(&reader, 20, SEEK_SET) != 0); assert(_z_bytes_reader_seek(&reader, -20, SEEK_SET) != 0); assert(_z_bytes_reader_seek(&reader, 0, SEEK_END) == 0); assert(_z_bytes_reader_tell(&reader) == 10); assert(_z_bytes_reader_seek(&reader, -3, SEEK_END) == 0); assert(_z_bytes_reader_tell(&reader) == 7); assert(_z_bytes_reader_seek(&reader, -8, SEEK_END) == 0); assert(_z_bytes_reader_tell(&reader) == 2); assert(_z_bytes_reader_seek(&reader, -10, SEEK_END) == 0); assert(_z_bytes_reader_tell(&reader) == 0); assert(_z_bytes_reader_seek(&reader, -20, SEEK_END) != 0); assert(_z_bytes_reader_seek(&reader, 5, SEEK_END) != 0); _z_bytes_drop(&b); } void test_writer(void) { uint8_t data1[5] = {1, 2, 3, 4, 5}; uint8_t data2[5] = {1, 2, 6, 7, 8}; uint8_t data3[3] = {3, 9, 10}; uint8_t data1_out[5] = {1, 2, 3, 4, 5}; uint8_t data2_out[8] = {1, 2, 6, 7, 8, 3, 9, 10}; _z_bytes_writer_t writer = _z_bytes_writer_empty(); _z_bytes_writer_write_all(&writer, data1, 5); _z_bytes_writer_write_all(&writer, data2, 5); _z_bytes_writer_write_all(&writer, data3, 3); _z_bytes_t b = _z_bytes_writer_finish(&writer); assert(_z_bytes_len(&b) == 13); assert(_z_bytes_num_slices(&b) == 2); assert(_z_arc_slice_len(_z_bytes_get_slice(&b, 0)) == 5); assert(_z_arc_slice_len(_z_bytes_get_slice(&b, 1)) == 8); assert(memcmp(data1_out, _z_arc_slice_data(_z_bytes_get_slice(&b, 0)), 5) == 0); assert(memcmp(data2_out, _z_arc_slice_data(_z_bytes_get_slice(&b, 1)), 8) == 0); _z_bytes_drop(&b); } int main(void) { test_null_bytes(); test_slice(); test_append(); test_reader_read(); test_reader_seek(); test_writer(); return 0; } ================================================ FILE: tests/z_cancellation_token_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico.h" #undef NDEBUG #include typedef struct { size_t called; size_t dropped; } token_handler_arg_t; z_result_t cancel_callback(void* arg) { token_handler_arg_t* typed = (token_handler_arg_t*)arg; (typed->called)++; return Z_OK; } void cancel_drop(void* arg) { token_handler_arg_t* typed = (token_handler_arg_t*)arg; (typed->dropped)++; } void test_cancellation_token_cancel(void) { _z_cancellation_token_t ct; assert(_z_cancellation_token_create(&ct) == Z_OK); token_handler_arg_t arg1 = {0}; token_handler_arg_t arg2 = {0}; _z_cancellation_token_on_cancel_handler_t h1 = { ._arg = &arg1, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_cancellation_token_on_cancel_handler_t h2 = { ._arg = &arg2, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_sync_group_notifier_t notifier; assert(_z_cancellation_token_get_notifier(&ct, ¬ifier) == _Z_RES_OK); size_t id1, id2; assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h1, &id1) == Z_OK); assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h2, &id2) == Z_OK); _z_sync_group_notifier_drop(¬ifier); assert(!_z_cancellation_token_is_cancelled(&ct)); assert(_z_cancellation_token_cancel(&ct) == Z_OK); assert(_z_cancellation_token_is_cancelled(&ct)); assert(arg1.called == 1); assert(arg1.dropped == 1); assert(arg2.called == 1); assert(arg2.dropped == 1); _z_cancellation_token_clear(&ct); } void test_cancellation_token_cancel_timeout(void) { _z_cancellation_token_t ct; assert(_z_cancellation_token_create(&ct) == Z_OK); token_handler_arg_t arg1 = {0}; token_handler_arg_t arg2 = {0}; _z_cancellation_token_on_cancel_handler_t h1 = { ._arg = &arg1, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_cancellation_token_on_cancel_handler_t h2 = { ._arg = &arg2, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_sync_group_notifier_t notifier; assert(_z_cancellation_token_get_notifier(&ct, ¬ifier) == _Z_RES_OK); size_t id1, id2; assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h1, &id1) == Z_OK); assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h2, &id2) == Z_OK); assert(!_z_cancellation_token_is_cancelled(&ct)); assert(_z_cancellation_token_cancel_with_timeout(&ct, 1000) == Z_ETIMEDOUT); assert(!_z_cancellation_token_is_cancelled(&ct)); _z_sync_group_notifier_drop(¬ifier); assert(_z_cancellation_token_cancel_with_timeout(&ct, 1000) == _Z_RES_OK); assert(_z_cancellation_token_is_cancelled(&ct)); _z_cancellation_token_clear(&ct); } void test_cancellation_token_remove_handler(void) { _z_cancellation_token_t ct; assert(_z_cancellation_token_create(&ct) == Z_OK); token_handler_arg_t arg1 = {0}; token_handler_arg_t arg2 = {0}; _z_cancellation_token_on_cancel_handler_t h1 = { ._arg = &arg1, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_cancellation_token_on_cancel_handler_t h2 = { ._arg = &arg2, ._on_cancel = cancel_callback, ._on_drop = cancel_drop}; _z_sync_group_notifier_t notifier; assert(_z_cancellation_token_get_notifier(&ct, ¬ifier) == _Z_RES_OK); size_t id1, id2; assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h1, &id1) == Z_OK); assert(_z_cancellation_token_add_on_cancel_handler(&ct, &h2, &id2) == Z_OK); assert(_z_cancellation_token_remove_on_cancel_handler(&ct, id2) == Z_OK); assert(arg2.dropped == 1); _z_sync_group_notifier_drop(¬ifier); assert(!_z_cancellation_token_is_cancelled(&ct)); assert(_z_cancellation_token_cancel(&ct) == Z_OK); assert(_z_cancellation_token_is_cancelled(&ct)); assert(arg1.called == 1); assert(arg1.dropped == 1); assert(arg2.called == 0); _z_cancellation_token_clear(&ct); } int main(void) { test_cancellation_token_cancel(); test_cancellation_token_cancel_timeout(); test_cancellation_token_remove_handler(); return 0; } ================================================ FILE: tests/z_channels_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico/api/handlers.h" #include "zenoh-pico/api/macros.h" #include "zenoh-pico/collections/bytes.h" #undef NDEBUG #include #define SEND(closure, v) \ do { \ _z_bytes_t payload; \ _z_slice_t slice = {.start = (const uint8_t *)v, .len = strlen(v)}; \ _z_bytes_from_slice(&payload, &slice); \ z_loaned_sample_t sample = { \ .keyexpr = _z_declared_keyexpr_alias_from_str("key"), \ .payload = payload, \ .timestamp = _z_timestamp_null(), \ .encoding = _z_encoding_null(), \ .kind = 0, \ .qos = {0}, \ .attachment = _z_bytes_null(), \ }; \ z_call(*z_loan(closure), &sample); \ } while (0); #define _RECV(handler, method, buf) \ do { \ z_owned_sample_t sample; \ z_result_t res = method(z_loan(handler), &sample); \ if (res == Z_CHANNEL_DISCONNECTED) { \ strcpy(buf, "closed"); \ } else if (res == Z_OK) { \ z_owned_slice_t value; \ z_bytes_to_slice(z_sample_payload(z_loan(sample)), &value); \ size_t value_len = z_slice_len(z_loan(value)); \ strncpy(buf, (const char *)z_slice_data(z_loan(value)), value_len); \ buf[value_len] = '\0'; \ z_drop(z_move(sample)); \ z_drop(z_move(value)); \ } else if (res == Z_CHANNEL_NODATA) { \ strcpy(buf, "nodata"); \ } \ } while (0); #define RECV(handler, buf) _RECV(handler, z_recv, buf) #define TRY_RECV(handler, buf) _RECV(handler, z_try_recv, buf) void sample_fifo_channel_test(void) { z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; z_fifo_channel_sample_new(&closure, &handler, 10); SEND(closure, "v1") SEND(closure, "v22") SEND(closure, "v333") SEND(closure, "v4444") char buf[100]; RECV(handler, buf) assert(strcmp(buf, "v1") == 0); RECV(handler, buf) assert(strcmp(buf, "v22") == 0); RECV(handler, buf) assert(strcmp(buf, "v333") == 0); RECV(handler, buf) assert(strcmp(buf, "v4444") == 0); z_drop(z_move(closure)); RECV(handler, buf) assert(strcmp(buf, "closed") == 0); z_drop(z_move(handler)); } void sample_fifo_channel_test_try_recv(void) { z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; z_fifo_channel_sample_new(&closure, &handler, 10); char buf[100]; TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); SEND(closure, "v1") SEND(closure, "v22") SEND(closure, "v333") SEND(closure, "v4444") TRY_RECV(handler, buf) assert(strcmp(buf, "v1") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "v22") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "v333") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "v4444") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); z_drop(z_move(closure)); TRY_RECV(handler, buf) assert(strcmp(buf, "closed") == 0); z_drop(z_move(handler)); } void sample_ring_channel_test_in_size(void) { z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, 10); char buf[100]; TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); SEND(closure, "v1") SEND(closure, "v22") SEND(closure, "v333") SEND(closure, "v4444") RECV(handler, buf) assert(strcmp(buf, "v1") == 0); RECV(handler, buf) assert(strcmp(buf, "v22") == 0); RECV(handler, buf) assert(strcmp(buf, "v333") == 0); RECV(handler, buf) assert(strcmp(buf, "v4444") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); z_drop(z_move(closure)); RECV(handler, buf) assert(strcmp(buf, "closed") == 0); z_drop(z_move(handler)); } void sample_ring_channel_test_over_size(void) { z_owned_closure_sample_t closure; z_owned_ring_handler_sample_t handler; z_ring_channel_sample_new(&closure, &handler, 3); char buf[100]; TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); SEND(closure, "v1") SEND(closure, "v22") SEND(closure, "v333") SEND(closure, "v4444") RECV(handler, buf) assert(strcmp(buf, "v22") == 0); RECV(handler, buf) assert(strcmp(buf, "v333") == 0); RECV(handler, buf) assert(strcmp(buf, "v4444") == 0); TRY_RECV(handler, buf) assert(strcmp(buf, "nodata") == 0); z_drop(z_move(closure)); TRY_RECV(handler, buf) assert(strcmp(buf, "closed") == 0); z_drop(z_move(handler)); } void zero_size_test(void) { z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t fifo_handler; assert(z_fifo_channel_sample_new(&closure, &fifo_handler, 0) != Z_OK); assert(z_fifo_channel_sample_new(&closure, &fifo_handler, 1) == Z_OK); z_drop(z_move(closure)); z_drop(z_move(fifo_handler)); z_owned_ring_handler_sample_t ring_handler; assert(z_ring_channel_sample_new(&closure, &ring_handler, 0) != Z_OK); assert(z_ring_channel_sample_new(&closure, &ring_handler, 1) == Z_OK); z_drop(z_move(closure)); z_drop(z_move(ring_handler)); } int main(void) { sample_fifo_channel_test(); sample_fifo_channel_test_try_recv(); sample_ring_channel_test_in_size(); sample_ring_channel_test_over_size(); zero_size_test(); } ================================================ FILE: tests/z_client_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/utils/uuid.h" #undef NDEBUG #include #define MSG 1000 #define MSG_LEN 1024 #define FRAGMENT_MSG_NB 100 #define FRAGMENT_MSG_LEN 100000 #define QRY 100 #define QRY_CLT 10 #define SET 100 #define SLEEP 1 #define TIMEOUT 60 const char *uri = "demo/example/"; unsigned int idx[SET]; // The active resource, subscriber, queryable declarations _z_list_t *pubs1 = NULL; z_owned_keyexpr_t rids1[SET]; _z_list_t *subs2 = NULL; _z_list_t *qles2 = NULL; z_owned_keyexpr_t rids2[SET]; volatile unsigned int total = 0; volatile unsigned int queries = 0; void query_handler(z_loaned_query_t *query, void *arg) { char *res = (char *)malloc(64); snprintf(res, 64, "%s%u", uri, *(unsigned int *)arg); printf(">> Received query: %s\t(%u/%u)\n", res, queries, total); z_view_string_t k_str, res_str; z_keyexpr_as_view_string(z_query_keyexpr(query), &k_str); z_view_string_from_str(&res_str, res); assert(_z_string_equals(z_loan(k_str), z_loan(res_str))); z_view_string_t pred; z_query_parameters(query, &pred); assert(z_string_len(z_loan(pred)) == 0); // Reply value encoding z_owned_bytes_t reply_payload; z_bytes_copy_from_str(&reply_payload, res); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); queries++; free(res); } volatile unsigned int replies = 0; void reply_handler(z_loaned_reply_t *reply, void *arg) { char *res = (char *)malloc(64); snprintf(res, 64, "%s%u", uri, *(unsigned int *)arg); if (z_reply_is_ok(reply)) { const z_loaned_sample_t *sample = z_reply_ok(reply); printf(">> Received reply data: %s\t(%u/%u)\n", res, replies, total); z_view_string_t k_str, res_str; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k_str); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); assert(z_string_len(z_loan(value)) == strlen(res)); assert(strncmp(res, z_string_data(z_loan(value)), strlen(res)) == 0); z_view_string_from_str(&res_str, res); assert(_z_string_equals(z_loan(k_str), z_loan(res_str))); replies++; z_drop(z_move(value)); } else { printf(">> Received an error\n"); } free(res); } volatile unsigned int datas = 0; void data_handler(z_loaned_sample_t *sample, void *arg) { char *res = (char *)malloc(64); snprintf(res, 64, "%s%u", uri, *(unsigned int *)arg); printf(">> Received data: %s\t(%u/%u)\n", res, datas, total); _z_string_t res_str = _z_string_alias_str(res); z_view_string_t k_str; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k_str); z_owned_slice_t value; z_bytes_to_slice(z_sample_payload(sample), &value); size_t payload_len = z_slice_len(z_loan(value)); assert((payload_len == MSG_LEN) || (payload_len == FRAGMENT_MSG_LEN)); assert(_z_string_equals(z_loan(k_str), &res_str)); datas++; z_drop(z_move(value)); free(res); } int main(int argc, char **argv) { setvbuf(stdout, NULL, _IOLBF, 1024); assert(argc >= 2); int is_reliable = strncmp(argv[1], "tcp", 3) == 0; unsigned int msg_count = MSG; for (int i = 2; i < argc; ++i) { if (strncmp(argv[i], "--msgs=", 7) == 0) { msg_count = (unsigned int)strtoul(argv[i] + 7, NULL, 10); } } z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, argv[1]); for (unsigned int i = 0; i < SET; i++) idx[i] = i; z_owned_session_t s1; assert(z_open(&s1, z_move(config), NULL) == Z_OK); _z_string_t zid1 = _z_id_to_string(&(_Z_RC_IN_VAL(z_loan(s1))->_local_zid)); printf("Session 1 with PID: %.*s\n", (int)_z_string_len(&zid1), _z_string_data(&zid1)); _z_string_clear(&zid1); z_sleep_s(SLEEP); z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, argv[1]); z_owned_session_t s2; assert(z_open(&s2, z_move(config), NULL) == Z_OK); assert(z_internal_check(s2)); _z_string_t zid2 = _z_id_to_string(&(_Z_RC_IN_VAL(z_loan(s2))->_local_zid)); printf("Session 2 with PID: %.*s\n", (int)_z_string_len(&zid2), _z_string_data(&zid2)); _z_string_clear(&zid2); z_sleep_s(SLEEP); // Declare resources on both sessions char *s1_res = (char *)malloc(64); for (unsigned int i = 0; i < SET; i++) { snprintf(s1_res, 64, "%s%u", uri, i); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, s1_res); z_owned_keyexpr_t expr; z_declare_keyexpr(z_loan(s1), &expr, z_loan(ke)); printf("Declared resource on session 1: %.*s\n", (int)z_string_len(&z_loan(expr)->_inner._keyexpr), z_string_data(&z_loan(expr)->_inner._keyexpr)); rids1[i] = expr; } z_sleep_s(SLEEP); for (unsigned int i = 0; i < SET; i++) { snprintf(s1_res, 64, "%s%u", uri, i); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, s1_res); z_owned_keyexpr_t expr; z_declare_keyexpr(z_loan(s2), &expr, z_loan(ke)); printf("Declared resource on session 2: %.*s\n", (int)z_string_len(&z_loan(expr)->_inner._keyexpr), z_string_data(&z_loan(expr)->_inner._keyexpr)); rids2[i] = expr; } z_sleep_s(SLEEP); // Declare subscribers and queryabales on second session for (unsigned int i = 0; i < SET; i++) { z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, &idx[i]); z_owned_subscriber_t *sub = (z_owned_subscriber_t *)z_malloc(sizeof(z_owned_subscriber_t)); z_result_t res = z_declare_subscriber(z_loan(s2), sub, z_loan(rids2[i]), z_move(callback), NULL); assert(res == _Z_RES_OK); printf("Declared subscription on session 2: %ju %.*s\n", (uintmax_t)z_subscriber_loan(sub)->_entity_id, (int)z_string_len(&z_loan(rids2[i])->_inner._keyexpr), z_string_data(&z_loan(rids2[i])->_inner._keyexpr)); subs2 = _z_list_push(subs2, sub); } z_sleep_s(SLEEP); for (unsigned int i = 0; i < SET; i++) { snprintf(s1_res, 64, "%s%u", uri, i); z_owned_closure_query_t callback; z_closure(&callback, query_handler, NULL, &idx[i]); z_owned_queryable_t *qle = (z_owned_queryable_t *)z_malloc(sizeof(z_owned_queryable_t)); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, s1_res); assert(z_declare_queryable(z_loan(s2), qle, z_loan(ke), z_move(callback), NULL) == _Z_RES_OK); printf("Declared queryable on session 2: %ju %i %s\n", (uintmax_t)qle->_val._entity_id, 0, s1_res); qles2 = _z_list_push(qles2, qle); } z_sleep_s(SLEEP); // Declare publisher on first session for (unsigned int i = 0; i < SET; i++) { z_owned_publisher_t *pub = (z_owned_publisher_t *)z_malloc(sizeof(z_owned_publisher_t)); if (z_declare_publisher(z_loan(s1), pub, z_loan(rids1[i]), NULL) < 0) { printf("Declared publisher on session 1: %zu\n", z_loan(*pub)->_id); } pubs1 = _z_list_push(pubs1, pub); } z_sleep_s(SLEEP); // Write data from first session size_t len = MSG_LEN; uint8_t *value = (uint8_t *)z_malloc(len); memset(value, 1, MSG_LEN); total = msg_count * SET; for (unsigned int n = 0; n < msg_count; n++) { for (unsigned int i = 0; i < SET; i++) { z_put_options_t opt; z_put_options_default(&opt); opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, len, NULL, NULL); z_put(z_loan(s1), z_loan(rids1[i]), z_move(payload), &opt); printf("Wrote data from session 1: %.*s %zu b\t(%u/%u)\n", (int)z_string_len(&z_loan(rids1[i])->_inner._keyexpr), z_string_data(&z_loan(rids1[i])->_inner._keyexpr), len, n * SET + (i + 1), total); } } // Wait to receive all the data z_clock_t now = z_clock_now(); unsigned int expected = is_reliable ? total : 1; while (datas < expected) { assert(z_clock_elapsed_s(&now) < TIMEOUT); printf("Waiting for datas... %u/%u\n", datas, expected); z_sleep_s(SLEEP); } if (is_reliable == true) assert(datas == expected); else assert(datas >= expected); datas = 0; z_sleep_s(SLEEP); // Write fragment data from first session if (is_reliable) { z_free((uint8_t *)value); len = FRAGMENT_MSG_LEN; value = (uint8_t *)z_malloc(len); memset(value, 1, FRAGMENT_MSG_LEN); total = FRAGMENT_MSG_NB * SET; for (unsigned int n = 0; n < FRAGMENT_MSG_NB; n++) { for (unsigned int i = 0; i < SET; i++) { z_put_options_t opt; z_put_options_default(&opt); opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, len, NULL, NULL); z_put(z_loan(s1), z_loan(rids1[i]), z_move(payload), &opt); printf("Wrote fragment data from session 1: %.*s %zu b\t(%u/%u)\n", (int)z_string_len(&z_loan(rids1[i])->_inner._keyexpr), z_string_data(&z_loan(rids1[i])->_inner._keyexpr), len, n * SET + (i + 1), total); } } // Wait to receive all the data now = z_clock_now(); while (datas < total) { assert(z_clock_elapsed_s(&now) < TIMEOUT); printf("Waiting for fragment datas... %u/%u\n", datas, total); z_sleep_s(SLEEP); } if (is_reliable == true) { assert(datas == total); } datas = 0; z_sleep_s(SLEEP); } // Query data from first session total = QRY * SET; for (unsigned int n = 0; n < QRY; n++) { for (unsigned int i = 0; i < SET; i++) { snprintf(s1_res, 64, "%s%u", uri, i); z_owned_closure_reply_t callback; z_closure(&callback, reply_handler, NULL, &idx[i]); z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, s1_res); z_get(z_loan(s1), z_loan(ke), "", z_move(callback), NULL); printf("Queried data from session 1: %i %s\n", 0, s1_res); } } // Wait to receive all the expected queries now = z_clock_now(); expected = is_reliable ? total : 1; while (queries < expected) { assert(z_clock_elapsed_s(&now) < TIMEOUT); printf("Waiting for queries... %u/%u\n", queries, expected); z_sleep_s(SLEEP); } if (is_reliable == true) assert(queries == expected); else assert(queries >= expected); queries = 0; // Wait to receive all the expected replies now = z_clock_now(); while (replies < expected) { assert(z_clock_elapsed_s(&now) < TIMEOUT); printf("Waiting for replies... %u/%u\n", replies, expected); z_sleep_s(SLEEP); } if (is_reliable == true) assert(replies == expected); else assert(replies >= expected); replies = 0; z_sleep_s(SLEEP); // Undeclare publishers on first session while (pubs1) { z_owned_publisher_t *pub = _z_list_value(pubs1); printf("Undeclared publisher on session 2: %zu\n", z_loan(*pub)->_id); z_drop(z_move(*pub)); pubs1 = _z_list_pop(pubs1, _z_noop_elem_free, NULL); } z_sleep_s(SLEEP); // Undeclare subscribers and queryables on second session while (subs2) { z_owned_subscriber_t *sub = _z_list_value(subs2); printf("Undeclared subscriber on session 2: %ju\n", (uintmax_t)sub->_val._entity_id); z_drop(z_move(*sub)); subs2 = _z_list_pop(subs2, _z_noop_elem_free, NULL); } z_sleep_s(SLEEP); while (qles2) { z_owned_queryable_t *qle = _z_list_value(qles2); printf("Undeclared queryable on session 2: %ju\n", (uintmax_t)qle->_val._entity_id); z_drop(z_move(*qle)); qles2 = _z_list_pop(qles2, _z_noop_elem_free, NULL); } z_sleep_s(SLEEP); // Undeclare resources on both sessions for (unsigned int i = 0; i < SET; i++) { printf("Undeclared resource on session 1: %.*s\n", (int)z_string_len(&z_loan(rids1[i])->_inner._keyexpr), z_string_data(&z_loan(rids1[i])->_inner._keyexpr)); z_undeclare_keyexpr(z_loan(s1), z_move(rids1[i])); } z_sleep_s(SLEEP); for (unsigned int i = 0; i < SET; i++) { printf("Undeclared resource on session 2: %.*s\n", (int)z_string_len(&z_loan(rids2[i])->_inner._keyexpr), z_string_data(&z_loan(rids2[i])->_inner._keyexpr)); z_undeclare_keyexpr(z_loan(s2), z_move(rids2[i])); } z_sleep_s(SLEEP); // Close both sessions printf("Closing session 1\n"); z_drop(z_move(s1)); z_sleep_s(SLEEP); printf("Closing session 2\n"); z_drop(z_move(s2)); z_free((uint8_t *)value); value = NULL; free(s1_res); return 0; } ================================================ FILE: tests/z_collections_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/collections/fifo.h" #include "zenoh-pico/collections/lifo.h" #include "zenoh-pico/collections/ring.h" #include "zenoh-pico/collections/sortedmap.h" #include "zenoh-pico/collections/string.h" #undef NDEBUG #include char *a = "a"; char *b = "b"; char *c = "c"; char *d = "d"; // RING _Z_RING_DEFINE(_z_str, char) // SORTED MAP _Z_SORTEDMAP_DEFINE(_z_str, _z_str, char, char) void print_ring(_z_str_ring_t *r) { printf("Ring { capacity: %zu, r_idx: %zu, w_idx: %zu, len: %zu }\n", _z_str_ring_capacity(r), r->_r_idx, r->_w_idx, _z_str_ring_len(r)); } void ring_test(void) { _z_str_ring_t r = _z_str_ring_make(3); print_ring(&r); assert(_z_str_ring_is_empty(&r)); // One char *s = _z_str_ring_push(&r, a); print_ring(&r); assert(s == NULL); assert(_z_str_ring_len(&r) == 1); s = _z_str_ring_pull(&r); print_ring(&r); assert(strcmp(a, s) == 0); assert(_z_str_ring_is_empty(&r)); s = _z_str_ring_pull(&r); print_ring(&r); assert(s == NULL); assert(_z_str_ring_is_empty(&r)); // Two s = _z_str_ring_push(&r, a); print_ring(&r); assert(s == NULL); assert(_z_str_ring_len(&r) == 1); s = _z_str_ring_push(&r, b); print_ring(&r); assert(s == NULL); assert(_z_str_ring_len(&r) == 2); s = _z_str_ring_push(&r, c); print_ring(&r); assert(s == NULL); assert(_z_str_ring_len(&r) == 3); assert(_z_str_ring_is_full(&r)); s = _z_str_ring_push(&r, d); print_ring(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_ring_len(&r) == 3); assert(_z_str_ring_is_full(&r)); s = _z_str_ring_push_force(&r, d); print_ring(&r); printf("%s == %s\n", a, s); assert(strcmp(a, s) == 0); assert(_z_str_ring_len(&r) == 3); assert(_z_str_ring_is_full(&r)); s = _z_str_ring_push_force(&r, d); print_ring(&r); printf("%s == %s\n", b, s); assert(strcmp(b, s) == 0); assert(_z_str_ring_len(&r) == 3); assert(_z_str_ring_is_full(&r)); s = _z_str_ring_push_force(&r, d); print_ring(&r); printf("%s == %s\n", c, s); assert(strcmp(c, s) == 0); assert(_z_str_ring_len(&r) == 3); assert(_z_str_ring_is_full(&r)); s = _z_str_ring_pull(&r); print_ring(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_ring_len(&r) == 2); s = _z_str_ring_pull(&r); print_ring(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_ring_len(&r) == 1); s = _z_str_ring_pull(&r); print_ring(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_ring_is_empty(&r)); _z_str_ring_clear(&r); } void ring_test_init_free(void) { _z_str_ring_t *r = (_z_str_ring_t *)malloc(sizeof(_z_str_ring_t)); _z_str_ring_init(r, 1); assert(r != NULL); char *str = (char *)calloc(1, sizeof(char)); _z_str_ring_push_force_drop(r, str); _z_str_ring_free(&r); assert(r == NULL); } void ring_iterator_test(void) { #define TEST_RING(ring, values, n) \ { \ _z_str_ring_iterator_t iter = _z_str_ring_iterator_make(&ring); \ _z_str_ring_reverse_iterator_t reverse_iter = _z_str_ring_reverse_iterator_make(&ring); \ \ for (int i = 0; i < n; i++) { \ assert(_z_str_ring_iterator_next(&iter)); \ assert(strcmp(_z_str_ring_iterator_value(&iter), values[i]) == 0); \ } \ \ for (int i = n - 1; i >= 0; i--) { \ assert(_z_str_ring_reverse_iterator_next(&reverse_iter)); \ assert(strcmp(_z_str_ring_reverse_iterator_value(&reverse_iter), values[i]) == 0); \ } \ \ assert(!_z_str_ring_iterator_next(&iter)); \ assert(!_z_str_ring_reverse_iterator_next(&reverse_iter)); \ } _z_str_ring_t ring = _z_str_ring_make(4); char *values[] = {"A", "B", "C", "D", "E", "F"}; assert(_z_str_ring_push(&ring, values[0]) == NULL); // { "A" } TEST_RING(ring, values, 1); assert(_z_str_ring_push(&ring, values[1]) == NULL); // { "A", "B" } TEST_RING(ring, values, 2); assert(_z_str_ring_push(&ring, values[2]) == NULL); // { "A", "B", "C" } TEST_RING(ring, values, 3); assert(_z_str_ring_push(&ring, values[3]) == NULL); // { "A", "B", "C", "D" } TEST_RING(ring, values, 4); assert(_z_str_ring_pull(&ring) != NULL); // { "B", "C", "D" } TEST_RING(ring, (values + 1), 3); assert(_z_str_ring_push(&ring, values[4]) == NULL); // { "B", "C", "D", "E" } TEST_RING(ring, (values + 1), 4); assert(_z_str_ring_pull(&ring) != NULL); // { "C", "D", "E" } TEST_RING(ring, (values + 2), 3); assert(_z_str_ring_push(&ring, values[5]) == NULL); // { "C", "D", "E", "F" } TEST_RING(ring, (values + 2), 4); assert(_z_str_ring_pull(&ring) != NULL); // { "D", "E", "F" } TEST_RING(ring, (values + 3), 3); assert(_z_str_ring_pull(&ring) != NULL); // { "E", "F" } TEST_RING(ring, (values + 4), 2); assert(_z_str_ring_pull(&ring) != NULL); // { "F" } TEST_RING(ring, (values + 5), 1); assert(_z_str_ring_pull(&ring) != NULL); // {} TEST_RING(ring, values, 0); _z_str_ring_clear(&ring); #undef TEST_RING } // LIFO _Z_LIFO_DEFINE(_z_str, char) void print_lifo(_z_str_lifo_t *r) { printf("Lifo { capacity: %zu, len: %zu }\n", _z_str_lifo_capacity(r), _z_str_lifo_len(r)); } void lifo_test(void) { _z_str_lifo_t r = _z_str_lifo_make(3); print_lifo(&r); assert(_z_str_lifo_is_empty(&r)); // One char *s = _z_str_lifo_push(&r, a); print_lifo(&r); assert(s == NULL); assert(_z_str_lifo_len(&r) == 1); s = _z_str_lifo_pull(&r); print_lifo(&r); printf("%s == %s\n", a, s); assert(strcmp(a, s) == 0); assert(_z_str_lifo_is_empty(&r)); s = _z_str_lifo_pull(&r); print_lifo(&r); assert(s == NULL); assert(_z_str_lifo_is_empty(&r)); // Two s = _z_str_lifo_push(&r, a); print_lifo(&r); assert(s == NULL); assert(_z_str_lifo_len(&r) == 1); s = _z_str_lifo_push(&r, b); print_lifo(&r); assert(s == NULL); assert(_z_str_lifo_len(&r) == 2); s = _z_str_lifo_push(&r, c); print_lifo(&r); assert(s == NULL); assert(_z_str_lifo_len(&r) == 3); assert(_z_str_lifo_is_full(&r)); s = _z_str_lifo_push(&r, d); print_lifo(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_lifo_len(&r) == 3); assert(_z_str_lifo_is_full(&r)); s = _z_str_lifo_pull(&r); print_lifo(&r); printf("%s == %s\n", c, s); assert(strcmp(c, s) == 0); assert(_z_str_lifo_len(&r) == 2); s = _z_str_lifo_pull(&r); print_lifo(&r); printf("%s == %s\n", b, s); assert(strcmp(b, s) == 0); assert(_z_str_lifo_len(&r) == 1); s = _z_str_lifo_pull(&r); print_lifo(&r); printf("%s == %s\n", a, s); assert(strcmp(a, s) == 0); assert(_z_str_lifo_is_empty(&r)); _z_str_lifo_clear(&r); } void lifo_test_init_free(void) { _z_str_lifo_t *r = (_z_str_lifo_t *)malloc(sizeof(_z_str_lifo_t)); _z_str_lifo_init(r, 1); assert(r != NULL); char *str = (char *)calloc(1, sizeof(char)); _z_str_lifo_push_drop(r, str); _z_str_lifo_free(&r); assert(r == NULL); } // FIFO _Z_FIFO_DEFINE(_z_str, char) void print_fifo(_z_str_fifo_t *r) { printf("Fifo { capacity: %zu, len: %zu }\n", _z_str_fifo_capacity(r), _z_str_fifo_len(r)); } void fifo_test(void) { _z_str_fifo_t r = _z_str_fifo_make(3); print_fifo(&r); assert(_z_str_fifo_is_empty(&r)); // One char *s = _z_str_fifo_push(&r, a); print_fifo(&r); assert(s == NULL); assert(_z_str_fifo_len(&r) == 1); s = _z_str_fifo_pull(&r); print_fifo(&r); printf("%s == %s\n", a, s); assert(strcmp(a, s) == 0); assert(_z_str_fifo_is_empty(&r)); s = _z_str_fifo_pull(&r); print_fifo(&r); assert(s == NULL); assert(_z_str_fifo_is_empty(&r)); // Two s = _z_str_fifo_push(&r, a); print_fifo(&r); assert(s == NULL); assert(_z_str_fifo_len(&r) == 1); s = _z_str_fifo_push(&r, b); print_fifo(&r); assert(s == NULL); assert(_z_str_fifo_len(&r) == 2); s = _z_str_fifo_push(&r, c); print_fifo(&r); assert(s == NULL); assert(_z_str_fifo_len(&r) == 3); assert(_z_str_fifo_is_full(&r)); s = _z_str_fifo_push(&r, d); print_fifo(&r); printf("%s == %s\n", d, s); assert(strcmp(d, s) == 0); assert(_z_str_fifo_len(&r) == 3); assert(_z_str_fifo_is_full(&r)); s = _z_str_fifo_pull(&r); print_fifo(&r); printf("%s == %s\n", a, s); assert(strcmp(a, s) == 0); assert(_z_str_fifo_len(&r) == 2); s = _z_str_fifo_pull(&r); print_fifo(&r); printf("%s == %s\n", b, s); assert(strcmp(b, s) == 0); assert(_z_str_fifo_len(&r) == 1); s = _z_str_fifo_pull(&r); print_fifo(&r); printf("%s == %s\n", c, s); assert(strcmp(c, s) == 0); assert(_z_str_fifo_is_empty(&r)); _z_str_fifo_clear(&r); } void fifo_test_init_free(void) { _z_str_fifo_t *r = (_z_str_fifo_t *)malloc(sizeof(_z_str_fifo_t)); _z_str_fifo_init(r, 1); assert(r != NULL); char *str = (char *)calloc(1, sizeof(char)); _z_str_fifo_push_drop(r, str); _z_str_fifo_free(&r); assert(r == NULL); } void int_map_iterator_test(void) { _z_str_intmap_t map; map = _z_str_intmap_make(); _z_str_intmap_insert(&map, 10, _z_str_clone("A")); _z_str_intmap_insert(&map, 20, _z_str_clone("B")); _z_str_intmap_insert(&map, 30, _z_str_clone("C")); _z_str_intmap_insert(&map, 40, _z_str_clone("D")); #define TEST_MAP(map) \ { \ _z_str_intmap_iterator_t iter = _z_str_intmap_iterator_make(&map); \ assert(_z_str_intmap_iterator_next(&iter)); \ assert(_z_str_intmap_iterator_key(&iter) == 20); \ assert(strcmp(_z_str_intmap_iterator_value(&iter), "B") == 0); \ \ assert(_z_str_intmap_iterator_next(&iter)); \ assert(_z_str_intmap_iterator_key(&iter) == 40); \ assert(strcmp(_z_str_intmap_iterator_value(&iter), "D") == 0); \ \ assert(_z_str_intmap_iterator_next(&iter)); \ assert(_z_str_intmap_iterator_key(&iter) == 10); \ assert(strcmp(_z_str_intmap_iterator_value(&iter), "A") == 0); \ \ assert(_z_str_intmap_iterator_next(&iter)); \ assert(_z_str_intmap_iterator_key(&iter) == 30); \ assert(strcmp(_z_str_intmap_iterator_value(&iter), "C") == 0); \ \ assert(!_z_str_intmap_iterator_next(&iter)); \ } TEST_MAP(map); _z_str_intmap_t map2 = _z_str_intmap_clone(&map); TEST_MAP(map2); _z_str_intmap_clear(&map); _z_str_intmap_clear(&map2); #undef TEST_MAP } void int_map_iterator_deletion_test(void) { _z_str_intmap_t map; map = _z_str_intmap_make(); _z_str_intmap_insert(&map, 10, _z_str_clone("A")); _z_str_intmap_insert(&map, 20, _z_str_clone("B")); _z_str_intmap_insert(&map, 30, _z_str_clone("C")); _z_str_intmap_insert(&map, 40, _z_str_clone("D")); _z_str_intmap_iterator_t iter = _z_str_intmap_iterator_make(&map); _z_str_intmap_iterator_next(&iter); for (size_t s = 4; s != 0; s--) { assert(s == _z_str_intmap_len(&map)); size_t key = _z_str_intmap_iterator_key(&iter); assert(strlen(_z_str_intmap_iterator_value(&iter)) == 1); _z_str_intmap_iterator_next(&iter); _z_str_intmap_remove(&map, key); } _z_str_intmap_clear(&map); } void int_map_extract_test(void) { _z_str_intmap_t map; map = _z_str_intmap_make(); _z_str_intmap_insert(&map, 10, _z_str_clone("A")); _z_str_intmap_insert(&map, 20, _z_str_clone("B")); _z_str_intmap_insert(&map, 30, _z_str_clone("C")); _z_str_intmap_insert(&map, 40, _z_str_clone("D")); assert(_z_str_intmap_extract(&map, 100) == NULL); char *item_c = _z_str_intmap_extract(&map, 30); assert(strcmp(item_c, "C") == 0); z_free(item_c); assert(_z_str_intmap_len(&map) == 3); assert(_z_str_intmap_get(&map, 30) == NULL); assert(_z_str_intmap_extract(&map, 30) == NULL); _z_str_intmap_clear(&map); } static bool slist_eq_f(const void *left, const void *right) { return strcmp((char *)left, (char *)right) == 0; } static bool slist_starts_with_f(const void *left, const void *right) { // SAFETY: left and right are guaranteed to be null-terminated. // Flawfinder: ignore [CWE-126] return strncmp((char *)left, (char *)right, strlen((char *)left)) == 0; } void slist_test(void) { char *values[] = {"test1", "test2", "test3"}; _z_slist_t *slist = NULL; // Test empty assert(_z_slist_is_empty(slist)); assert(_z_slist_len(slist) == 0); // Add value test slist = _z_slist_push(slist, values[0], strlen(values[0]) + 1, _z_noop_copy, false); assert(!_z_slist_is_empty(slist)); assert(_z_slist_len(slist) == 1); assert(strcmp(values[0], (char *)_z_slist_value(slist)) == 0); assert(_z_slist_next(slist) == NULL); slist = _z_slist_push(slist, values[1], strlen(values[1]) + 1, _z_noop_copy, false); assert(_z_slist_len(slist) == 2); assert(strcmp(values[1], (char *)_z_slist_value(slist)) == 0); assert(strcmp(values[0], (char *)_z_slist_value(_z_slist_next(slist))) == 0); // Push back test slist = _z_slist_push_back(slist, values[2], strlen(values[2]) + 1, _z_noop_copy, false); assert(_z_slist_len(slist) == 3); assert(strcmp(values[2], (char *)_z_slist_value(_z_slist_next(_z_slist_next(slist)))) == 0); // Pop test slist = _z_slist_pop(slist, _z_noop_clear); assert(_z_slist_len(slist) == 2); assert(strcmp(values[0], (char *)_z_slist_value(slist)) == 0); assert(strcmp(values[2], (char *)_z_slist_value(_z_slist_next(slist))) == 0); slist = _z_slist_pop(slist, _z_noop_clear); assert(_z_slist_len(slist) == 1); assert(strcmp(values[2], (char *)_z_slist_value(slist)) == 0); assert(strlen(values[2]) == 5); slist = _z_slist_pop(slist, _z_noop_clear); assert(_z_slist_is_empty(slist)); // Drop element test for (size_t i = 0; i < _ZP_ARRAY_SIZE(values); i++) { slist = _z_slist_push(slist, values[i], strlen(values[i]) + 1, _z_noop_copy, false); } // Drop second element slist = _z_slist_drop_element(slist, slist, _z_noop_clear); assert(_z_slist_len(slist) == 2); assert(strcmp(values[2], (char *)_z_slist_value(slist)) == 0); assert(strcmp(values[0], (char *)_z_slist_value(_z_slist_next(slist))) == 0); // Free test _z_slist_free(&slist, _z_noop_clear); assert(_z_slist_is_empty(slist)); // Push empty test slist = _z_slist_push_empty(slist, strlen(values[0]) + 1); assert(_z_slist_len(slist) == 1); char *val = (char *)_z_slist_value(slist); strcpy(val, values[0]); assert(strcmp(values[0], (char *)_z_slist_value(slist)) == 0); _z_slist_free(&slist, _z_noop_clear); // Drop filter test for (size_t i = 0; i < _ZP_ARRAY_SIZE(values); i++) { slist = _z_slist_push(slist, values[i], strlen(values[i]) + 1, _z_noop_copy, false); } slist = _z_slist_drop_filter(slist, _z_noop_clear, slist_eq_f, values[1], true); assert(_z_slist_len(slist) == 2); assert(strcmp(values[2], (char *)_z_slist_value(slist)) == 0); assert(strcmp(values[0], (char *)_z_slist_value(_z_slist_next(slist))) == 0); _z_slist_free(&slist, _z_noop_clear); // Find test for (size_t i = 0; i < _ZP_ARRAY_SIZE(values); i++) { slist = _z_slist_push(slist, values[i], strlen(values[i]) + 1, _z_noop_copy, false); } _z_slist_t *elem = NULL; elem = _z_slist_find(slist, slist_eq_f, "bob"); assert(elem == NULL); elem = _z_slist_find(slist, slist_eq_f, values[2]); assert(elem != NULL); _z_slist_free(&slist, _z_noop_clear); // Extract test char *values2[] = {"test1", "tes2", "test3"}; for (size_t i = 0; i < _ZP_ARRAY_SIZE(values2); i++) { // SAFETY: values2[i] only contains null-terminated strings. // Flawfinder: ignore [CWE-126] slist = _z_slist_push(slist, values2[i], strlen(values2[i]) + 1, _z_noop_copy, false); } _z_slist_t *extracted = NULL; slist = _z_slist_extract_filter(slist, slist_starts_with_f, "test", &extracted, false); assert(_z_slist_len(slist) == 1); assert(_z_slist_len(extracted) == 2); elem = NULL; elem = _z_slist_find(extracted, slist_eq_f, "test1"); assert(elem != NULL); elem = _z_slist_find(extracted, slist_eq_f, "test3"); assert(elem != NULL); elem = _z_slist_find(slist, slist_eq_f, "tes2"); assert(elem != NULL); _z_slist_free(&slist, _z_noop_clear); _z_slist_free(&extracted, _z_noop_clear); for (size_t i = 0; i < _ZP_ARRAY_SIZE(values2); i++) { // SAFETY: values2[i] only contains null-terminated strings. // Flawfinder: ignore [CWE-126] slist = _z_slist_push(slist, values2[i], strlen(values2[i]) + 1, _z_noop_copy, false); } slist = _z_slist_extract_filter(slist, slist_starts_with_f, "test", &extracted, true); assert(_z_slist_len(slist) == 2); assert(_z_slist_len(extracted) == 1); elem = _z_slist_find(slist, slist_eq_f, "tes2"); assert(elem != NULL); _z_slist_free(&slist, _z_noop_clear); _z_slist_free(&extracted, _z_noop_clear); // Clone test for (size_t i = 0; i < _ZP_ARRAY_SIZE(values); i++) { slist = _z_slist_push(slist, values[i], strlen(values[i]) + 1, _z_noop_copy, false); } _z_slist_t *clone = NULL; clone = _z_slist_clone(slist, strlen(values[0]), _z_noop_copy, false); assert(_z_slist_len(slist) == 3); assert(strcmp(values[2], (char *)_z_slist_value(slist)) == 0); assert(strcmp(values[1], (char *)_z_slist_value(_z_slist_next(slist))) == 0); assert(strcmp(values[0], (char *)_z_slist_value(_z_slist_next(_z_slist_next(slist)))) == 0); _z_slist_free(&slist, _z_noop_clear); _z_slist_free(&clone, _z_noop_clear); } void sorted_map_iterator_test(void) { _z_str__z_str_sortedmap_t map; map = _z_str__z_str_sortedmap_make(); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("4"), _z_str_clone("D")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("1"), _z_str_clone("A")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("3"), _z_str_clone("C")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("2"), _z_str_clone("B")); #define TEST_SORTEDMAP(map) \ { \ assert(_z_str__z_str_sortedmap_len(&map) == 4); \ _z_str__z_str_sortedmap_iterator_t iter = _z_str__z_str_sortedmap_iterator_make(&map); \ \ assert(_z_str__z_str_sortedmap_iterator_next(&iter)); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_key(&iter), "1") == 0); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_value(&iter), "A") == 0); \ \ assert(_z_str__z_str_sortedmap_iterator_next(&iter)); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_key(&iter), "2") == 0); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_value(&iter), "B") == 0); \ \ assert(_z_str__z_str_sortedmap_iterator_next(&iter)); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_key(&iter), "3") == 0); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_value(&iter), "C") == 0); \ \ assert(_z_str__z_str_sortedmap_iterator_next(&iter)); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_key(&iter), "4") == 0); \ assert(strcmp(_z_str__z_str_sortedmap_iterator_value(&iter), "D") == 0); \ \ assert(!_z_str__z_str_sortedmap_iterator_next(&iter)); \ } TEST_SORTEDMAP(map); _z_str__z_str_sortedmap_t map2 = _z_str__z_str_sortedmap_clone(&map); TEST_SORTEDMAP(map2); _z_str__z_str_sortedmap_clear(&map); _z_str__z_str_sortedmap_clear(&map2); #undef TEST_SORTEDMAP } void sorted_map_iterator_deletion_test(void) { _z_str__z_str_sortedmap_t map; map = _z_str__z_str_sortedmap_make(); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("4"), _z_str_clone("D")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("1"), _z_str_clone("A")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("3"), _z_str_clone("C")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("2"), _z_str_clone("B")); _z_str__z_str_sortedmap_iterator_t iter = _z_str__z_str_sortedmap_iterator_make(&map); _z_str__z_str_sortedmap_iterator_next(&iter); for (size_t s = 4; s != 0; s--) { assert(s == _z_str__z_str_sortedmap_len(&map)); char *key = _z_str__z_str_sortedmap_iterator_key(&iter); // SAFETY: returned _z_str_t should be null-terminated. // Flawfinder: ignore [CWE-126] assert(strlen(_z_str__z_str_sortedmap_iterator_value(&iter)) == 1); _z_str__z_str_sortedmap_iterator_next(&iter); _z_str__z_str_sortedmap_remove(&map, key); } _z_str__z_str_sortedmap_clear(&map); } void sorted_map_replace_test(void) { _z_str__z_str_sortedmap_t map = _z_str__z_str_sortedmap_make(); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("key"), _z_str_clone("old")); assert(strcmp(_z_str__z_str_sortedmap_get(&map, "key"), "old") == 0); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("key"), _z_str_clone("new")); assert(strcmp(_z_str__z_str_sortedmap_get(&map, "key"), "new") == 0); _z_str__z_str_sortedmap_clear(&map); } void sorted_map_missing_key_test(void) { _z_str__z_str_sortedmap_t map = _z_str__z_str_sortedmap_make(); assert(_z_str__z_str_sortedmap_get(&map, "absent") == NULL); _z_str__z_str_sortedmap_clear(&map); } void sorted_map_empty_test(void) { _z_str__z_str_sortedmap_t map = _z_str__z_str_sortedmap_make(); assert(_z_str__z_str_sortedmap_is_empty(&map)); assert(_z_str__z_str_sortedmap_len(&map) == 0); _z_str__z_str_sortedmap_iterator_t iter = _z_str__z_str_sortedmap_iterator_make(&map); assert(!_z_str__z_str_sortedmap_iterator_next(&iter)); _z_str__z_str_sortedmap_clear(&map); } void sorted_map_pop_first_test(void) { _z_str__z_str_sortedmap_t map; _z_str__z_str_sortedmap_init(&map); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("3"), _z_str_clone("three")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("1"), _z_str_clone("one")); _z_str__z_str_sortedmap_insert(&map, _z_str_clone("2"), _z_str_clone("two")); _z_str__z_str_sortedmap_entry_t *entry = _z_str__z_str_sortedmap_pop_first(&map); assert(entry != NULL); assert(strcmp(_z_str__z_str_sortedmap_entry_key(entry), "1") == 0); assert(strcmp(_z_str__z_str_sortedmap_entry_val(entry), "one") == 0); _z_str__z_str_sortedmap_entry_free(&entry); assert(_z_str__z_str_sortedmap_len(&map) == 2); _z_str__z_str_sortedmap_clear(&map); } void sorted_map_copy_move_test(void) { _z_str__z_str_sortedmap_t src = _z_str__z_str_sortedmap_make(); _z_str__z_str_sortedmap_insert(&src, _z_str_clone("key"), _z_str_clone("value")); _z_str__z_str_sortedmap_t dst = _z_str__z_str_sortedmap_make(); assert(_z_str__z_str_sortedmap_copy(&dst, &src) == _Z_RES_OK); assert(strcmp(_z_str__z_str_sortedmap_get(&dst, "key"), "value") == 0); _z_str__z_str_sortedmap_clear(&src); assert(_z_str__z_str_sortedmap_move(&src, &dst) == _Z_RES_OK); assert(strcmp(_z_str__z_str_sortedmap_get(&src, "key"), "value") == 0); _z_str__z_str_sortedmap_clear(&src); } void sorted_map_free_test(void) { _z_str__z_str_sortedmap_t *map_ptr = z_malloc(sizeof(_z_str__z_str_sortedmap_t)); assert(map_ptr != NULL); *map_ptr = _z_str__z_str_sortedmap_make(); _z_str__z_str_sortedmap_insert(map_ptr, _z_str_clone("K"), _z_str_clone("V")); _z_str__z_str_sortedmap_free(&map_ptr); assert(map_ptr == NULL); // should be nullified after free } void sorted_map_stress_test(void) { _z_str__z_str_sortedmap_t map = _z_str__z_str_sortedmap_make(); char key[16], val[16]; for (int i = 100; i >= 1; i--) { snprintf(key, sizeof(key), "%03d", i); snprintf(val, sizeof(val), "val%d", i); _z_str__z_str_sortedmap_insert(&map, _z_str_clone(key), _z_str_clone(val)); } _z_str__z_str_sortedmap_iterator_t iter = _z_str__z_str_sortedmap_iterator_make(&map); int expected = 1; while (_z_str__z_str_sortedmap_iterator_next(&iter)) { char expected_key[16]; snprintf(expected_key, sizeof(expected_key), "%03d", expected); assert(strcmp(_z_str__z_str_sortedmap_iterator_key(&iter), expected_key) == 0); expected++; } assert(expected == 101); // 1..100 _z_str__z_str_sortedmap_clear(&map); } size_t destroyed_elts = 0; typedef struct { int id; } _z_elt_t; void _z_elt_destroy(_z_elt_t *elt) { (void)elt; destroyed_elts++; } void _z_elt_move(_z_elt_t *dst, _z_elt_t *src) { dst->id = src->id; src->id = -1; // Invalidate source to detect use-after-move } #define _ZP_DEQUE_TEMPLATE_ELEM_TYPE _z_elt_t #define _ZP_DEQUE_TEMPLATE_NAME _z_elt_deque #define _ZP_DEQUE_TEMPLATE_ELEM_DESTROY_FN_NAME _z_elt_destroy #define _ZP_DEQUE_TEMPLATE_ELEM_MOVE_FN_NAME _z_elt_move #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME _z_elt_compare #define _ZP_DEQUE_TEMPLATE_SIZE 4 #include "zenoh-pico/collections/deque_template.h" void deque_test(void) { destroyed_elts = 0; _z_elt_deque_t deque = _z_elt_deque_new(); assert(_z_elt_deque_size(&deque) == 0); assert(_z_elt_deque_is_empty(&deque)); assert(_z_elt_deque_front(&deque) == NULL); assert(_z_elt_deque_back(&deque) == NULL); _z_elt_t elt0 = {.id = 0}; assert(_z_elt_deque_push_front(&deque, &elt0)); assert(_z_elt_deque_front(&deque)->id == 0); assert(_z_elt_deque_back(&deque)->id == 0); _z_elt_t elt1 = {.id = 1}; assert(_z_elt_deque_push_front(&deque, &elt1)); _z_elt_t elt2 = {.id = 2}; assert(_z_elt_deque_push_back(&deque, &elt2)); assert(_z_elt_deque_front(&deque)->id == 1); assert(_z_elt_deque_back(&deque)->id == 2); _z_elt_t elt3 = {.id = 3}; assert(_z_elt_deque_push_back(&deque, &elt3)); assert(_z_elt_deque_front(&deque)->id == 1); assert(_z_elt_deque_back(&deque)->id == 3); assert(_z_elt_deque_size(&deque) == 4); assert(!_z_elt_deque_is_empty(&deque)); _z_elt_t elt4 = {.id = 4}; assert(!_z_elt_deque_push_back(&deque, &elt4)); assert(_z_elt_deque_front(&deque)->id == 1); assert(_z_elt_deque_back(&deque)->id == 3); _z_elt_t out; assert(_z_elt_deque_pop_front(&deque, &out)); assert(out.id == 1); assert(_z_elt_deque_front(&deque)->id == 0); assert(_z_elt_deque_back(&deque)->id == 3); assert(_z_elt_deque_pop_back(&deque, &out)); assert(out.id == 3); assert(_z_elt_deque_front(&deque)->id == 0); assert(_z_elt_deque_back(&deque)->id == 2); assert(_z_elt_deque_size(&deque) == 2); // Clean up remaining elements _z_elt_deque_destroy(&deque); assert(_z_elt_deque_size(&deque) == 0); assert(destroyed_elts == 2); } int main(void) { ring_test(); ring_test_init_free(); lifo_test(); lifo_test_init_free(); fifo_test(); fifo_test_init_free(); int_map_iterator_test(); int_map_iterator_deletion_test(); int_map_extract_test(); ring_iterator_test(); slist_test(); sorted_map_iterator_test(); sorted_map_iterator_deletion_test(); sorted_map_replace_test(); sorted_map_missing_key_test(); sorted_map_empty_test(); sorted_map_pop_first_test(); sorted_map_copy_move_test(); sorted_map_free_test(); sorted_map_stress_test(); deque_test(); } ================================================ FILE: tests/z_condvar_wait_until_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/system/platform.h" #undef NDEBUG #include #if Z_FEATURE_MULTI_THREAD == 1 typedef struct { z_loaned_mutex_t* m; z_loaned_condvar_t* cv; } task_arg_t; void* task_fn(void* arg) { task_arg_t* typed = (task_arg_t*)arg; z_mutex_lock(typed->m); z_sleep_s(2); z_condvar_signal(typed->cv); z_mutex_unlock(typed->m); return NULL; } #endif int main(void) { #if Z_FEATURE_MULTI_THREAD == 1 z_owned_condvar_t cv; z_owned_mutex_t m; z_mutex_init(&m); z_condvar_init(&cv); assert(z_mutex_lock(z_mutex_loan_mut(&m)) == Z_OK); for (unsigned i = 1; i <= 5; i++) { printf("Check timedout wait: %d seconds\n", i); z_clock_t c = z_clock_now(); z_clock_t deadline = c; z_clock_advance_s(&deadline, i); assert(z_condvar_wait_until(z_condvar_loan_mut(&cv), z_mutex_loan_mut(&m), &deadline) == Z_ETIMEDOUT); ; unsigned long elapsed = z_clock_elapsed_ms(&c); assert(elapsed > (i * 1000 - 500) && elapsed < (i * 1000 + 500)); } printf("Check successful wait\n"); z_clock_t c = z_clock_now(); z_clock_t deadline = c; z_clock_advance_s(&deadline, 10); z_owned_task_t task; task_arg_t arg; arg.m = z_mutex_loan_mut(&m); arg.cv = z_condvar_loan_mut(&cv); assert(z_task_init(&task, NULL, task_fn, &arg) == Z_OK); assert(z_condvar_wait_until(z_condvar_loan_mut(&cv), z_mutex_loan_mut(&m), &deadline) == Z_OK); unsigned long elapsed = z_clock_elapsed_ms(&c); assert(elapsed < 5000); z_task_join(z_task_move(&task)); z_mutex_unlock(z_mutex_loan_mut(&m)); z_condvar_drop(z_condvar_move(&cv)); z_mutex_drop(z_mutex_move(&m)); #endif } ================================================ FILE: tests/z_data_struct_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/transport/transport.h" #undef NDEBUG #include void str_vec_list_intmap_test(void) { char *s = (char *)malloc(64); size_t len = 128; // str-vec printf(">>> str-vec\r\n"); _z_str_vec_t vec = _z_str_vec_make(1); assert(_z_str_vec_is_empty(&vec) == true); for (size_t i = 0; i < len; i++) { snprintf(s, 64, "%zu", i); _z_str_vec_append(&vec, _z_str_clone(s)); char *e = _z_str_vec_get(&vec, i); printf("append(%zu) = %s\r\n", i, e); assert(_z_str_eq(s, e) == true); _z_str_vec_set(&vec, i, _z_str_clone(s)); e = _z_str_vec_get(&vec, i); printf("set(%zu) = %s\r\n", i, e); assert(_z_str_eq(s, e) == true); assert(_z_str_vec_len(&vec) == i + 1); } assert(_z_str_vec_len(&vec) == len); _z_str_vec_clear(&vec); assert(_z_str_vec_is_empty(&vec) == true); // str-list printf(">>> str-list\r\n"); _z_str_list_t *list = _z_str_list_new(); assert(_z_str_list_is_empty(list) == true); for (size_t i = 0; i < len; i++) { snprintf(s, 64, "%zu", i); list = _z_str_list_push(list, _z_str_clone(s)); char *e = _z_str_list_value(list); printf("push(%zu) = %s\r\n", i, e); assert(_z_str_eq(s, e) == true); assert(_z_str_list_len(list) == i + 1); } assert(_z_str_list_len(list) == len); for (size_t i = 0; i < len; i++) { snprintf(s, 64, "%zu", i); list = _z_str_list_pop(list, NULL); assert(_z_str_list_len(list) == len - (i + 1)); } assert(_z_str_list_is_empty(list) == true); for (size_t i = 0; i < len; i++) { snprintf(s, 64, "%zu", i); list = _z_str_list_push(list, _z_str_clone(s)); assert(_z_str_eq(s, _z_str_list_value(list)) == true); } assert(_z_str_list_len(list) == len); _z_str_list_free(&list); assert(_z_str_list_is_empty(list) == true); // str-intmap printf(">>> str-intmap\r\n"); _z_str_intmap_t map = _z_str_intmap_make(); assert(_z_str_intmap_is_empty(&map) == true); for (size_t i = 0; i < len; i++) { snprintf(s, 64, "%zu", i); _z_str_intmap_insert(&map, i, _z_str_clone(s)); char *e = _z_str_intmap_get(&map, i); printf("get(%zu) = %s\r\n", i, e); assert(_z_str_eq(s, e) == true); assert(_z_str_intmap_len(&map) == i + 1); } assert(_z_str_intmap_len(&map) == len); for (size_t i = 0; i < len; i++) { _z_str_intmap_remove(&map, i); assert(_z_str_intmap_get(&map, i) == NULL); assert(_z_str_intmap_len(&map) == (len - 1) - i); } assert(_z_str_intmap_is_empty(&map) == true); _z_str_intmap_clear(&map); assert(_z_str_intmap_is_empty(&map) == true); z_free(s); } void _z_slice_custom_deleter(void *data, void *context) { _ZP_UNUSED(data); size_t *cnt = (size_t *)context; (*cnt)++; } void z_slice_custom_delete_test(void) { size_t counter = 0; uint8_t data[5] = {1, 2, 3, 4, 5}; _z_delete_context_t dc = (_z_delete_context_t){.deleter = _z_slice_custom_deleter, .context = &counter}; _z_slice_t s1 = _z_slice_from_buf_custom_deleter(data, 5, dc); _z_slice_t s2 = _z_slice_from_buf_custom_deleter(data, 5, dc); _z_slice_t s3 = _z_slice_copy_from_buf(data, 5); _z_slice_t s4 = _z_slice_alias_buf(data, 5); assert(_z_slice_is_alloced(&s1)); assert(_z_slice_is_alloced(&s2)); assert(_z_slice_is_alloced(&s3)); assert(!_z_slice_is_alloced(&s4)); _z_slice_clear(&s1); _z_slice_clear(&s2); _z_slice_clear(&s3); _z_slice_clear(&s4); assert(counter == 2); } void z_string_array_test(void) { // create new array z_owned_string_array_t a; z_string_array_new(&a); char s1[] = "string1"; char s2[] = "string2"; char s3[] = "string3"; char s4[] = "string4"; // add by copy z_view_string_t vs1; z_view_string_from_str(&vs1, s1); assert(z_string_array_push_by_copy(z_string_array_loan_mut(&a), z_view_string_loan(&vs1)) == 1); z_view_string_t vs2; z_view_string_from_str(&vs2, s2); assert(z_string_array_push_by_copy(z_string_array_loan_mut(&a), z_view_string_loan(&vs2)) == 2); // add by alias z_view_string_t vs3; z_view_string_from_str(&vs3, s3); assert(z_string_array_push_by_alias(z_string_array_loan_mut(&a), z_view_string_loan(&vs3)) == 3); z_view_string_t vs4; z_view_string_from_str(&vs4, s4); assert(z_string_array_push_by_alias(z_string_array_loan_mut(&a), z_view_string_loan(&vs4)) == 4); // check values const z_loaned_string_t *ls1 = z_string_array_get(z_string_array_loan(&a), 0); assert(strncmp(z_string_data(ls1), s1, z_string_len(ls1)) == 0); const z_loaned_string_t *ls2 = z_string_array_get(z_string_array_loan(&a), 1); assert(strncmp(z_string_data(ls2), s2, z_string_len(ls2)) == 0); const z_loaned_string_t *ls3 = z_string_array_get(z_string_array_loan(&a), 2); assert(strncmp(z_string_data(ls3), s3, z_string_len(ls3)) == 0); const z_loaned_string_t *ls4 = z_string_array_get(z_string_array_loan(&a), 3); assert(strncmp(z_string_data(ls4), s4, z_string_len(ls4)) == 0); // modify original strings values s1[0] = 'X'; s2[0] = 'X'; s3[0] = 'X'; s4[0] = 'X'; // values passed by copy should NOT be changed ls1 = z_string_array_get(z_string_array_loan(&a), 0); assert(strncmp(z_string_data(ls1), "string1", z_string_len(ls1)) == 0); ls2 = z_string_array_get(z_string_array_loan(&a), 1); assert(strncmp(z_string_data(ls2), "string2", z_string_len(ls2)) == 0); // values passed by alias should be changed ls3 = z_string_array_get(z_string_array_loan(&a), 2); assert(strncmp(z_string_data(ls3), s3, z_string_len(ls3)) == 0); ls4 = z_string_array_get(z_string_array_loan(&a), 3); assert(strncmp(z_string_data(ls4), s4, z_string_len(ls4)) == 0); // cleanup z_string_array_drop(z_string_array_move(&a)); } void z_id_to_string_test(void) { z_id_t id; for (uint8_t i = 0; i < sizeof(id.id); i++) { id.id[i] = i; } z_owned_string_t id_str; z_id_to_string(&id, &id_str); assert(z_string_len(z_string_loan(&id_str)) == 32); assert(strncmp("0f0e0d0c0b0a09080706050403020100", z_string_data(z_string_loan(&id_str)), z_string_len(z_string_loan(&id_str))) == 0); z_string_drop(z_string_move(&id_str)); } int main(void) { str_vec_list_intmap_test(); z_slice_custom_delete_test(); z_string_array_test(); z_id_to_string_test(); return 0; } ================================================ FILE: tests/z_endpoint_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/link/config/udp.h" #include "zenoh-pico/link/endpoint.h" #include "zenoh-pico/utils/result.h" #if Z_FEATURE_LINK_WS == 1 #include "zenoh-pico/link/config/ws.h" #endif #if Z_FEATURE_LINK_TLS == 1 #include "zenoh-pico/link/config/tls.h" #endif #undef NDEBUG #include int main(void) { // Locator printf(">>> Testing locators...\n"); _z_locator_t lc; _z_string_t str = _z_string_alias_str("tcp/127.0.0.1:7447"); assert(_z_locator_from_string(&lc, &str) == _Z_RES_OK); str = _z_string_alias_str("tcp"); assert(_z_string_equals(&lc._protocol, &str) == true); str = _z_string_alias_str("127.0.0.1:7447"); assert(_z_string_equals(&lc._address, &str) == true); assert(_z_str_intmap_is_empty(&lc._metadata) == true); _z_locator_clear(&lc); str = _z_string_alias_str(""); assert(_z_locator_from_string(&lc, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_locator_clear(&lc); str = _z_string_alias_str("/"); assert(_z_locator_from_string(&lc, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_locator_clear(&lc); str = _z_string_alias_str("tcp"); assert(_z_locator_from_string(&lc, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_locator_clear(&lc); str = _z_string_alias_str("tcp/"); assert(_z_locator_from_string(&lc, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_locator_clear(&lc); str = _z_string_alias_str("127.0.0.1:7447"); assert(_z_locator_from_string(&lc, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_locator_clear(&lc); str = _z_string_alias_str("tcp/127.0.0.1:7447?"); assert(_z_locator_from_string(&lc, &str) == _Z_RES_OK); _z_locator_clear(&lc); // No metadata defined so far... but this is a valid syntax in principle str = _z_string_alias_str("tcp/127.0.0.1:7447?invalid=ctrl"); assert(_z_locator_from_string(&lc, &str) == _Z_RES_OK); _z_locator_clear(&lc); // Endpoint printf(">>> Testing endpoints...\n"); _z_endpoint_t ep; str = _z_string_alias_str("tcp/127.0.0.1:7447"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); str = _z_string_alias_str("tcp"); assert(_z_string_equals(&ep._locator._protocol, &str) == true); str = _z_string_alias_str("127.0.0.1:7447"); assert(_z_string_equals(&ep._locator._address, &str) == true); assert(_z_str_intmap_is_empty(&ep._locator._metadata) == true); assert(_z_str_intmap_is_empty(&ep._config) == true); _z_endpoint_clear(&ep); str = _z_string_alias_str(""); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("/"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("tcp"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("tcp/"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("127.0.0.1:7447"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("tcp/127.0.0.1:7447?"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); // No metadata defined so far... but this is a valid syntax in principle str = _z_string_alias_str("tcp/127.0.0.1:7447?invalid=ctrl"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("udp/127.0.0.1:7447#iface=eth0"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); str = _z_string_alias_str("udp"); assert(_z_string_equals(&ep._locator._protocol, &str) == true); str = _z_string_alias_str("127.0.0.1:7447"); assert(_z_string_equals(&ep._locator._address, &str) == true); assert(_z_str_intmap_is_empty(&ep._locator._metadata) == true); assert(_z_str_intmap_len(&ep._config) == 1); char *p = _z_str_intmap_get(&ep._config, UDP_CONFIG_IFACE_KEY); assert(_z_str_eq(p, "eth0") == true); (void)(p); _z_endpoint_clear(&ep); str = _z_string_alias_str("udp/127.0.0.1:7447#invalid=eth0"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("udp/127.0.0.1:7447?invalid=ctrl#iface=eth0"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("udp/127.0.0.1:7447?invalid=ctrl#invalid=eth0"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); #if Z_FEATURE_LINK_WS == 1 str = _z_string_alias_str("ws/localhost:7447#tout=1000"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); str = _z_string_alias_str("ws"); assert(_z_string_equals(&ep._locator._protocol, &str) == true); str = _z_string_alias_str("localhost:7447"); assert(_z_string_equals(&ep._locator._address, &str) == true); assert(_z_str_intmap_len(&ep._config) == 1); char *tout = _z_str_intmap_get(&ep._config, WS_CONFIG_TOUT_KEY); assert(_z_str_eq(tout, "1000") == true); _z_endpoint_clear(&ep); str = _z_string_alias_str("ws/[::1]:7447"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("ws/"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); #endif #if Z_FEATURE_LINK_TLS == 1 str = _z_string_alias_str("tls/localhost:7447#root_ca_certificate=/path/ca.pem"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); str = _z_string_alias_str("tls"); assert(_z_string_equals(&ep._locator._protocol, &str) == true); str = _z_string_alias_str("localhost:7447"); assert(_z_string_equals(&ep._locator._address, &str) == true); assert(_z_str_intmap_len(&ep._config) == 1); char *ca_path = _z_str_intmap_get(&ep._config, TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY); assert(_z_str_eq(ca_path, "/path/ca.pem") == true); _z_endpoint_clear(&ep); str = _z_string_alias_str("tls/[::1]:7447"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("tls/localhost:7447#invalid=value"); assert(_z_endpoint_from_string(&ep, &str) == _Z_RES_OK); _z_endpoint_clear(&ep); str = _z_string_alias_str("tls/"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); str = _z_string_alias_str("tls"); assert(_z_endpoint_from_string(&ep, &str) == _Z_ERR_CONFIG_LOCATOR_INVALID); _z_endpoint_clear(&ep); #endif return 0; } ================================================ FILE: tests/z_executor_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include "zenoh-pico/runtime/executor.h" #include "zenoh-pico/system/platform.h" #undef NDEBUG #include // ─── Helpers ──────────────────────────────────────────────────────────────── typedef struct { int call_count; bool destroyed; } test_arg_t; // Finishes immediately static _z_fut_fn_result_t fn_finish(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; a->call_count++; return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } // Reschedules with a wake_up_time, finishes on second call static _z_fut_fn_result_t fn_reschedule_timed(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; a->call_count++; if (a->call_count == 1) { z_clock_t wake = z_clock_now(); z_clock_advance_ms(&wake, 500); // wake up after 500ms return (_z_fut_fn_result_t){ ._status = _Z_FUT_STATUS_SLEEPING, ._wake_up_time = wake, }; } return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } // Reschedules without wake_up_time (goes back to regular deque), finishes on second call static _z_fut_fn_result_t fn_reschedule_deque(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; a->call_count++; if (a->call_count == 1) { return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_RUNNING}; } return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } // Spawns a child future into the executor, then finishes static _z_fut_fn_result_t fn_spawn_child(void *arg, _z_executor_t *ex) { test_arg_t *child_arg = (test_arg_t *)arg; _z_fut_t child = _z_fut_new(child_arg, fn_finish, NULL); _z_executor_spawn(ex, &child); return (_z_fut_fn_result_t){._status = _Z_FUT_STATUS_READY}; } static void destroy_fn(void *arg) { test_arg_t *a = (test_arg_t *)arg; a->destroyed = true; } // Suspends on first call; caller must resume it externally; finishes on second call. static _z_fut_fn_result_t fn_suspend_once(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; a->call_count++; if (a->call_count == 1) { return _z_fut_fn_result_suspend(); } return _z_fut_fn_result_ready(); } // Suspends indefinitely (never returns READY on its own). static _z_fut_fn_result_t fn_suspend_forever(void *arg, _z_executor_t *ex) { (void)ex; test_arg_t *a = (test_arg_t *)arg; a->call_count++; return _z_fut_fn_result_suspend(); } // Drain the executor until NO_TASKS or max_spins reached; return number of spins static int drain(_z_executor_t *ex, int max_spins) { int spins = 0; while (spins++ < max_spins) { _z_executor_spin_result_t r = _z_executor_spin(ex); if (r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS) break; } return spins; } // ─── Tests ─────────────────────────────────────────────────────────────────── // Spin on a freshly created executor returns NO_TASKS. static void test_spin_empty(void) { printf("Test: spin on empty executor returns NO_TASKS\n"); _z_executor_t ex = _z_executor_new(); _z_executor_spin_result_t r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // A future with a handle: status transitions RUNNING → READY. static void test_spawn_with_handle_status_transitions(void) { printf("Test: future with handle transitions RUNNING -> READY\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_RUNNING); drain(&ex, 10); assert(arg.call_count == 1); assert(arg.destroyed == true); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); _z_executor_destroy(&ex); } // A future returning SLEEPING is re-queued in the timed pqueue. static void test_timed_reschedule(void) { printf("Test: timed reschedule re-queues in timed pqueue and eventually finishes\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_reschedule_timed, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); // First spin: task runs once, reschedules with immediate wake_up_time _z_executor_spin_result_t r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(arg.call_count == 1); assert(arg.destroyed == false); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_SLEEPING); r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT); z_clock_t now = z_clock_now(); assert(zp_clock_elapsed_ms_since(&r.next_wake_up_time, &now) > 300); z_sleep_ms(100); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_SHOULD_WAIT); assert(zp_clock_elapsed_ms_since(&r.next_wake_up_time, &now) > 200); z_sleep_ms(600); r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); assert(arg.call_count == 2); assert(arg.destroyed == true); r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // A future returning RUNNING is re-queued in the regular deque. static void test_deque_reschedule(void) { printf("Test: deque reschedule re-queues at back of deque and finishes on next spin\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_reschedule_deque, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); // First spin: task runs, returns not-ready, pushed back to deque _z_executor_spin_result_t r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(arg.call_count == 1); assert(arg.destroyed == false); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_RUNNING); // Second spin: task runs again and finishes r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(arg.call_count == 2); assert(arg.destroyed == true); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // Cancelling a handle before spin: body never runs, destroy_fn still called. static void test_cancel_before_spin(void) { printf("Test: cancel before spin prevents body; destroy_fn still called\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); _z_executor_cancel_fut(&ex, &h); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); // cancelled tasks are considered ready (not pending or running) drain(&ex, 10); assert(arg.call_count == 0); // body never ran assert(arg.destroyed == true); // destroy_fn still ran _z_executor_destroy(&ex); } // Cancelling after the task finishes is a safe no-op; status stays READY. static void test_cancel_after_finish(void) { printf("Test: cancel on READY handle is a no-op\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); drain(&ex, 10); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); // Must not crash or corrupt status assert(!_z_executor_cancel_fut(&ex, &h)); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); _z_executor_destroy(&ex); } // A task may spawn a child via the executor* passed to the fn. static void test_task_spawns_child(void) { printf("Test: task can spawn a child future via executor pointer\n"); _z_executor_t ex = _z_executor_new(); test_arg_t child_arg = {0}; _z_fut_t parent = _z_fut_new(&child_arg, fn_spawn_child, NULL); assert(!_z_fut_handle_is_null(_z_executor_spawn(&ex, &parent))); // First spin: parent runs, spawns child into executor _z_executor_spin(&ex); // Remaining spins: child runs drain(&ex, 10); assert(child_arg.call_count == 1); _z_executor_destroy(&ex); } // N independent tasks all complete when drained. static void test_multiple_tasks(void) { printf("Test: multiple futures all complete\n"); _z_executor_t ex = _z_executor_new(); const int N = 8; test_arg_t args[8]; for (int i = 0; i < N; i++) { args[i] = (test_arg_t){0}; _z_fut_t fut = _z_fut_new(&args[i], fn_finish, destroy_fn); assert(!_z_fut_handle_is_null(_z_executor_spawn(&ex, &fut))); } drain(&ex, N * 4); for (int i = 0; i < N; i++) { assert(args[i].call_count == 1); assert(args[i].destroyed == true); } assert(_z_executor_spin(&ex).status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // _z_executor_destroy calls destroy_fn on tasks that never ran. static void test_destroy_drains_pending(void) { printf("Test: _z_executor_destroy calls destroy_fn on all pending futures\n"); _z_executor_t ex = _z_executor_new(); const int N = 4; test_arg_t args[4]; for (int i = 0; i < N; i++) { args[i] = (test_arg_t){0}; _z_fut_t fut = _z_fut_new(&args[i], fn_finish, destroy_fn); assert(!_z_fut_handle_is_null(_z_executor_spawn(&ex, &fut))); } // Destroy without any spinning _z_executor_destroy(&ex); for (int i = 0; i < N; i++) { assert(args[i].call_count == 0); // body never ran assert(args[i].destroyed == true); // destroy_fn called by deque teardown } } // ─── Tests: suspend / resume ───────────────────────────────────────────────── // A task returning SUSPENDED is not run again until explicitly resumed. static void test_suspend_and_resume(void) { printf("Test: suspended task is skipped until resumed\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_suspend_once, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); // First spin: task runs and suspends — counts as EXECUTED_TASK. _z_executor_spin_result_t r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(arg.call_count == 1); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_SUSPENDED); // Additional spins do nothing — task is still suspended. r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); assert(arg.call_count == 1); // body must not have run again // Resume: task becomes runnable again. assert(_z_executor_resume_suspended_fut(&ex, &h)); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); // Next spin: task runs and finishes. r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_EXECUTED_TASK); assert(arg.call_count == 2); assert(arg.destroyed == true); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // Resuming a task that is not suspended is a safe no-op. static void test_resume_non_suspended_is_noop(void) { printf("Test: resume on non-suspended task is a no-op\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_finish, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); // Task is RUNNING (not yet executed), resume must be a no-op. assert(!_z_executor_resume_suspended_fut(&ex, &h)); drain(&ex, 10); assert(arg.call_count == 1); // Task is now gone (READY); resume must be a no-op. assert(!_z_executor_resume_suspended_fut(&ex, &h)); _z_executor_destroy(&ex); } // A suspended task can be cancelled; its destroy_fn is still called. static void test_cancel_suspended(void) { printf("Test: cancel on suspended task calls destroy_fn\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_suspend_forever, destroy_fn); _z_fut_handle_t h = _z_executor_spawn(&ex, &fut); assert(!_z_fut_handle_is_null(h)); // Run once: task suspends. _z_executor_spin(&ex); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_SUSPENDED); assert(arg.destroyed == false); // Cancel while suspended. assert(_z_executor_cancel_fut(&ex, &h)); assert(arg.destroyed == true); assert(_z_executor_get_fut_status(&ex, &h) == _Z_FUT_STATUS_READY); // Executor is now empty. _z_executor_spin_result_t r = _z_executor_spin(&ex); assert(r.status == _Z_EXECUTOR_SPIN_RESULT_NO_TASKS); _z_executor_destroy(&ex); } // Suspend does not starve other ready tasks: they run while one task is suspended. static void test_other_tasks_run_while_suspended(void) { printf("Test: other tasks run while one task is suspended\n"); _z_executor_t ex = _z_executor_new(); test_arg_t suspended_arg = {0}; test_arg_t other_arg = {0}; _z_fut_t suspended_fut = _z_fut_new(&suspended_arg, fn_suspend_once, NULL); _z_fut_handle_t suspended_h = _z_executor_spawn(&ex, &suspended_fut); _z_fut_t other_fut = _z_fut_new(&other_arg, fn_finish, destroy_fn); _z_executor_spawn(&ex, &other_fut); // Spin until suspended task suspends: the other task should also get a turn. drain(&ex, 10); assert(suspended_arg.call_count == 1); assert(_z_executor_get_fut_status(&ex, &suspended_h) == _Z_FUT_STATUS_SUSPENDED); assert(other_arg.call_count == 1); // other_fut must have run assert(other_arg.destroyed == true); _z_executor_resume_suspended_fut(&ex, &suspended_h); drain(&ex, 10); assert(suspended_arg.call_count == 2); _z_executor_destroy(&ex); } // destroy is called for suspended tasks that are still pending when executor is torn down. static void test_destroy_cleans_up_suspended(void) { printf("Test: _z_executor_destroy calls destroy_fn on suspended futures\n"); _z_executor_t ex = _z_executor_new(); test_arg_t arg = {0}; _z_fut_t fut = _z_fut_new(&arg, fn_suspend_forever, destroy_fn); _z_executor_spawn(&ex, &fut); _z_executor_spin(&ex); // task suspends assert(arg.destroyed == false); _z_executor_destroy(&ex); assert(arg.destroyed == true); } // ─── main ──────────────────────────────────────────────────────────────────── int main(void) { test_spin_empty(); test_spawn_with_handle_status_transitions(); test_timed_reschedule(); test_deque_reschedule(); test_cancel_before_spin(); test_cancel_after_finish(); test_task_spawns_child(); test_multiple_tasks(); test_destroy_drains_pending(); test_suspend_and_resume(); test_resume_non_suspended_is_noop(); test_cancel_suspended(); test_other_tasks_run_while_suspended(); test_destroy_cleans_up_suspended(); printf("All executor tests passed.\n"); return 0; } ================================================ FILE: tests/z_hashmap_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #undef NDEBUG #include // ── Instantiate uint32_t -> uint32_t, 8 buckets, capacity 12 ───────────────── static inline size_t u32_hash(const uint32_t *k) { uint32_t x = *k; x ^= x >> 16; x *= 0x45d9f3bU; x ^= x >> 16; return (size_t)x; } static inline bool u32_eq(const uint32_t *a, const uint32_t *b) { return *a == *b; } #define _ZP_HASHMAP_TEMPLATE_KEY_TYPE uint32_t #define _ZP_HASHMAP_TEMPLATE_VAL_TYPE uint32_t #define _ZP_HASHMAP_TEMPLATE_NAME u32map #define _ZP_HASHMAP_TEMPLATE_BUCKET_COUNT 8 #define _ZP_HASHMAP_TEMPLATE_CAPACITY 12 #define _ZP_HASHMAP_TEMPLATE_KEY_HASH_FN_NAME u32_hash #define _ZP_HASHMAP_TEMPLATE_KEY_EQ_FN_NAME u32_eq #include "zenoh-pico/collections/hashmap_template.h" // ── Tests ───────────────────────────────────────────────────────────────────── static void test_new_is_empty(void) { printf("Test: new map is empty\n"); u32map_t m = u32map_new(); assert(u32map_is_empty(&m)); assert(u32map_size(&m) == 0); u32map_destroy(&m); } static void test_insert_and_get(void) { printf("Test: insert then get returns value\n"); u32map_t m = u32map_new(); uint32_t k = 1, v = 10; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); assert(u32map_size(&m) == 1); uint32_t *got = u32map_get(&m, &(uint32_t){1}); assert(got != NULL && *got == 10); u32map_destroy(&m); } static void test_get_missing_returns_null(void) { printf("Test: get on missing key returns NULL\n"); u32map_t m = u32map_new(); assert(u32map_get(&m, &(uint32_t){42}) == NULL); u32map_destroy(&m); } static void test_insert_updates_existing(void) { printf("Test: insert with duplicate key updates value, size stays same\n"); u32map_t m = u32map_new(); uint32_t k = 5, v1 = 50, v2 = 99; assert(u32map_index_valid(u32map_insert(&m, &k, &v1))); assert(u32map_index_valid(u32map_insert(&m, &(uint32_t){5}, &v2))); assert(u32map_size(&m) == 1); assert(*u32map_get(&m, &(uint32_t){5}) == 99); u32map_destroy(&m); } static void test_remove_existing(void) { printf("Test: remove existing key moves value out\n"); u32map_t m = u32map_new(); uint32_t k = 7, v = 70; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); uint32_t out = 0; assert(u32map_remove(&m, &(uint32_t){7}, &out)); assert(out == 70); assert(u32map_size(&m) == 0); assert(u32map_get(&m, &(uint32_t){7}) == NULL); u32map_destroy(&m); } static void test_remove_missing_returns_false(void) { printf("Test: remove on missing key returns false\n"); u32map_t m = u32map_new(); assert(!u32map_remove(&m, &(uint32_t){99}, NULL)); u32map_destroy(&m); } static void test_remove_head_of_chain(void) { printf("Test: remove head of a collision chain; remaining entry still accessible\n"); u32map_t m = u32map_new(); // Keys 0 and 8 both hash to bucket 0 with BUCKET_COUNT=8 (hash%8 == 0 for both) uint32_t k0 = 0, v0 = 100; uint32_t k8 = 8, v8 = 800; assert(u32map_index_valid(u32map_insert(&m, &k0, &v0))); assert(u32map_index_valid(u32map_insert(&m, &k8, &v8))); assert(u32map_size(&m) == 2); // Remove whichever is the head (k8, inserted last → prepended) assert(u32map_remove(&m, &(uint32_t){8}, NULL)); assert(u32map_size(&m) == 1); assert(*u32map_get(&m, &(uint32_t){0}) == 100); assert(u32map_get(&m, &(uint32_t){8}) == NULL); u32map_destroy(&m); } static void test_remove_tail_of_chain(void) { printf("Test: remove tail of a collision chain; head still accessible\n"); u32map_t m = u32map_new(); uint32_t k0 = 0, v0 = 100; uint32_t k8 = 8, v8 = 800; assert(u32map_index_valid(u32map_insert(&m, &k0, &v0))); assert(u32map_index_valid(u32map_insert(&m, &k8, &v8))); assert(u32map_remove(&m, &(uint32_t){0}, NULL)); // tail (inserted first) assert(u32map_size(&m) == 1); assert(*u32map_get(&m, &(uint32_t){8}) == 800); assert(u32map_get(&m, &(uint32_t){0}) == NULL); u32map_destroy(&m); } static void test_contains(void) { printf("Test: contains\n"); u32map_t m = u32map_new(); assert(!u32map_contains(&m, &(uint32_t){3})); uint32_t k = 3, v = 30; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); assert(u32map_contains(&m, &(uint32_t){3})); assert(u32map_remove(&m, &(uint32_t){3}, NULL)); assert(!u32map_contains(&m, &(uint32_t){3})); u32map_destroy(&m); } static void test_clear_and_reuse(void) { printf("Test: clear empties the map and frees pool for reuse\n"); u32map_t m = u32map_new(); for (uint32_t i = 0; i < 12; i++) { uint32_t k = i, v = i * 10; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); } assert(u32map_size(&m) == 12); u32map_destroy(&m); assert(u32map_is_empty(&m)); // Pool fully freed — all 12 slots available again for (uint32_t i = 0; i < 12; i++) { uint32_t k = i + 100, v = i; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); } assert(u32map_size(&m) == 12); u32map_destroy(&m); } static void test_pool_exhaustion(void) { printf("Test: insert fails when pool is exhausted\n"); u32map_t m = u32map_new(); for (uint32_t i = 0; i < 12; i++) { uint32_t k = i, v = i; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); } // One more distinct key must fail uint32_t k = 200, v = 200; assert(!u32map_index_valid(u32map_insert(&m, &k, &v))); // Updating an existing key must still succeed (no new pool slot needed) uint32_t ku = 0, vu = 255; assert(u32map_index_valid(u32map_insert(&m, &ku, &vu))); assert(*u32map_get(&m, &(uint32_t){0}) == 255); u32map_destroy(&m); } static void test_pool_slot_reused_after_remove(void) { printf("Test: pool slot freed by remove is reused by subsequent insert\n"); u32map_t m = u32map_new(); // Fill to capacity for (uint32_t i = 0; i < 12; i++) { uint32_t k = i, v = i; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); } // Remove one entry to free a slot assert(u32map_remove(&m, &(uint32_t){0}, NULL)); assert(u32map_size(&m) == 11); // Now a new key must succeed uint32_t k = 200, v = 200; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); assert(u32map_size(&m) == 12); assert(*u32map_get(&m, &(uint32_t){200}) == 200); u32map_destroy(&m); } static void test_multiple_collisions(void) { printf("Test: many keys colliding into the same bucket\n"); u32map_t m = u32map_new(); // With BUCKET_COUNT=8, keys 0,8,16,24,32,40 all land in bucket 0 uint32_t keys[] = {0, 8, 16, 24, 32, 40}; for (size_t i = 0; i < 6; i++) { uint32_t k = keys[i], v = keys[i] * 10; assert(u32map_index_valid(u32map_insert(&m, &k, &v))); } assert(u32map_size(&m) == 6); for (size_t i = 0; i < 6; i++) { uint32_t *got = u32map_get(&m, &keys[i]); assert(got != NULL && *got == keys[i] * 10); } // Remove middle entries and verify survivors assert(u32map_remove(&m, &(uint32_t){16}, NULL)); assert(u32map_remove(&m, &(uint32_t){32}, NULL)); assert(u32map_size(&m) == 4); assert(u32map_get(&m, &(uint32_t){16}) == NULL); assert(u32map_get(&m, &(uint32_t){32}) == NULL); assert(*u32map_get(&m, &(uint32_t){0}) == 0); assert(*u32map_get(&m, &(uint32_t){8}) == 80); assert(*u32map_get(&m, &(uint32_t){24}) == 240); assert(*u32map_get(&m, &(uint32_t){40}) == 400); u32map_destroy(&m); } int main(void) { test_new_is_empty(); test_insert_and_get(); test_get_missing_returns_null(); test_insert_updates_existing(); test_remove_existing(); test_remove_missing_returns_false(); test_remove_head_of_chain(); test_remove_tail_of_chain(); test_contains(); test_clear_and_reuse(); test_pool_exhaustion(); test_pool_slot_reused_after_remove(); test_multiple_collisions(); return 0; } ================================================ FILE: tests/z_iobuf_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include "zenoh-pico/protocol/iobuf.h" #undef NDEBUG #include #define RUNS 1000 /*=============================*/ /* Printing functions */ /*=============================*/ void print_zbuf_overview(_z_zbuf_t *zbf) { printf(" ZBuf => Capacity: %zu\n", zbf->_ios._capacity); } void print_wbuf_overview(_z_wbuf_t *wbf) { printf(" WBuf => Expandable: %zu, Capacity: %zu\n", wbf->_expansion_step, _z_wbuf_capacity(wbf)); } void print_iosli(_z_iosli_t *ios) { printf("IOSli: Capacity: %zu, Rpos: %zu, Wpos: %zu, Buffer: [ ", ios->_capacity, ios->_r_pos, ios->_w_pos); for (size_t i = 0; i < ios->_w_pos; i++) { printf("%02x", ios->_buf[i]); if (i < ios->_capacity - 1) printf(" "); } printf(" ]"); } /*=============================*/ /* Generating functions */ /*=============================*/ int gen_bool(void) { return z_random_u8() % 2; } uint8_t gen_uint8(void) { return z_random_u8() % 255; } size_t gen_size_t(void) { size_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } _z_zbuf_t gen_zbuf(size_t len) { return _z_zbuf_make(len); } _z_wbuf_t gen_wbuf(size_t len) { bool is_expandable = false; if (gen_bool() == true) { is_expandable = true; len = 1 + (gen_size_t() % len); } _z_wbuf_t wbuf = _z_wbuf_make(len, is_expandable); assert(_z_wbuf_capacity(&wbuf) == len); return wbuf; } /*=============================*/ /* Tests */ /*=============================*/ void iosli_writable_readable(void) { size_t len = 128; _z_iosli_t ios = _z_iosli_make(len); _z_iosli_t *pios = _z_iosli_new(len); printf("\n>>> IOSli => Writable and Readable\n"); printf(" - IOSli write\n"); assert(_z_iosli_writable(&ios) == _z_iosli_writable(pios)); assert(_z_iosli_readable(&ios) == _z_iosli_readable(pios)); size_t writable = _z_iosli_writable(&ios); size_t readable = _z_iosli_readable(&ios); printf(" Writable: %zu\tReadable: %zu\n", writable, readable); assert(writable == len); assert(readable == 0); for (size_t i = 0; i < len; i++) { uint8_t byte = gen_uint8(); _z_iosli_write(&ios, byte); _z_iosli_write(pios, byte); assert(_z_iosli_writable(&ios) == _z_iosli_writable(pios)); assert(_z_iosli_readable(&ios) == _z_iosli_readable(pios)); writable = _z_iosli_writable(&ios); readable = _z_iosli_readable(&ios); printf(" Writable: %zu\tReadable: %zu\n", writable, readable); assert(writable == len - (i + 1)); assert(readable == i + 1); uint8_t b1 = _z_iosli_get(&ios, i); uint8_t b2 = _z_iosli_get(pios, i); assert(b1 == b2); (void)(b1); (void)(b2); } _z_iosli_t *cios = _z_iosli_clone(pios); assert(_z_iosli_writable(pios) == _z_iosli_writable(cios)); assert(_z_iosli_readable(pios) == _z_iosli_readable(cios)); printf(" - IOSli read\n"); for (size_t i = 0; i < len; i++) { uint8_t b1 = _z_iosli_read(&ios); uint8_t b2 = _z_iosli_read(pios); uint8_t b3 = _z_iosli_read(cios); assert(b1 == b2 && b2 == b3); (void)(b1); (void)(b2); (void)(b3); assert(_z_iosli_writable(&ios) == _z_iosli_writable(pios) && _z_iosli_writable(pios) == _z_iosli_writable(cios)); assert(_z_iosli_readable(&ios) == _z_iosli_readable(pios) && _z_iosli_readable(pios) == _z_iosli_readable(cios)); writable = _z_iosli_writable(&ios); readable = _z_iosli_readable(&ios); printf(" Writable: %zu\tReadable: %zu\n", writable, readable); assert(writable == 0); assert(readable == len - (i + 1)); } _z_iosli_reset(&ios); assert(_z_iosli_writable(&ios) == len); assert(_z_iosli_readable(&ios) == 0); _z_iosli_reset(pios); assert(_z_iosli_writable(pios) == len); assert(_z_iosli_readable(pios) == 0); _z_iosli_clear(cios); assert(_z_iosli_writable(cios) == 0); assert(_z_iosli_readable(cios) == 0); printf(" - IOSli bytes\n"); uint8_t *payload = (uint8_t *)z_malloc(len); memset((uint8_t *)payload, 1, len); for (size_t i = 0; i < len; i++) { payload[i] = gen_uint8(); _z_iosli_put(pios, payload[i], i); assert(payload[i] == _z_iosli_get(pios, i)); pios->_w_pos++; writable = _z_iosli_writable(pios); readable = _z_iosli_readable(pios); printf(" Writable: %zu\tReadable: %zu\n", writable, readable); assert(writable == len - (i + 1)); assert(readable == i + 1); } uint8_t *buffer = (uint8_t *)z_malloc(len); memset((uint8_t *)buffer, 1, len); _z_iosli_write_bytes(&ios, payload, 0, len); _z_iosli_read_bytes(&ios, buffer, 0, len); assert(memcmp(payload, buffer, len) == 0); memset(buffer, 0, len); _z_iosli_read_bytes(pios, buffer, 0, len); assert(memcmp(payload, buffer, len) == 0); memset(buffer, 0, len); _z_iosli_t wios = _z_iosli_wrap(payload, len, 0, len); _z_iosli_read_bytes(&wios, buffer, 0, len); assert(memcmp(payload, buffer, len) == 0); _z_iosli_clear(&ios); assert(_z_iosli_writable(&ios) == 0); assert(_z_iosli_readable(&ios) == 0); _z_iosli_clear(pios); assert(_z_iosli_writable(pios) == 0); assert(_z_iosli_readable(pios) == 0); _z_iosli_clear(&wios); assert(_z_iosli_writable(&wios) == 0); assert(_z_iosli_readable(&wios) == 0); _z_iosli_clear(pios); z_free(pios); _z_iosli_clear(cios); z_free(cios); z_free(buffer); z_free(payload); } void zbuf_writable_readable(void) { size_t len = 128; _z_zbuf_t zbf = _z_zbuf_make(len); printf("\n>>> ZBuf => Writable and Readable\n"); size_t writable = _z_zbuf_space_left(&zbf); printf(" Writable: %zu\n", writable); assert(writable == len); size_t readable = _z_zbuf_len(&zbf); printf(" Readable: %zu\n", readable); assert(readable == 0); _z_zbuf_set_wpos(&zbf, len); size_t read = 0; while (read < len) { size_t to_read = 1 + gen_size_t() % (len - read); for (size_t i = 0; i < to_read; i++) { _z_zbuf_read(&zbf); } read = read + to_read; writable = _z_zbuf_space_left(&zbf); printf(" Writable: %zu\n", writable); assert(writable == 0); readable = _z_zbuf_len(&zbf); printf(" Readable: %zu\n", readable); assert(readable == len - read); } _z_zbuf_clear(&zbf); } void zbuf_compact(void) { uint8_t len = 128; _z_zbuf_t zbf = _z_zbuf_make(len); printf("\n>>> ZBuf => Compact\n"); for (uint8_t i = 0; i < len; i++) { _z_iosli_write(&zbf._ios, i); } uint8_t counter = 0; while (counter < len) { size_t len01 = _z_zbuf_len(&zbf); _z_zbuf_compact(&zbf); assert(_z_zbuf_get_rpos(&zbf) == 0); assert(_z_zbuf_get_wpos(&zbf) == len01); assert(_z_zbuf_len(&zbf) == len01); printf(" Len: %zu, Rpos: %zu, Wpos: %zu\n", len01, _z_zbuf_get_rpos(&zbf), _z_zbuf_get_wpos(&zbf)); uint8_t vs = (uint8_t)(1 + gen_uint8() % (len - counter)); printf(" Read %u bytes => [", vs); for (uint8_t i = 0; i < vs; i++) { uint8_t l = counter++; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf(" ]\n"); } _z_zbuf_clear(&zbf); } void zbuf_view(void) { uint8_t len = 128; _z_zbuf_t zbf = _z_zbuf_make(len); printf("\n>>> ZBuf => View\n"); for (uint8_t i = 0; i < len; i++) { _z_iosli_write(&zbf._ios, i); } uint8_t counter = 0; while (counter < len) { uint8_t vs = (uint8_t)(1 + gen_uint8() % (len - counter)); _z_zbuf_t rv = _z_zbuf_view(&zbf, vs); printf(" View of %u bytes: ", vs); assert(_z_zbuf_capacity(&rv) == vs); assert(_z_zbuf_len(&rv) == vs); assert(_z_zbuf_space_left(&rv) == 0); printf("["); for (uint8_t i = 0; i < vs; i++) { uint8_t l = counter++; uint8_t r = _z_zbuf_read(&rv); printf(" %02x:%02x", l, r); assert(l == r); } printf(" ]\n"); _z_zbuf_set_rpos(&zbf, _z_zbuf_get_rpos(&zbf) + vs); } _z_zbuf_clear(&zbf); } void wbuf_writable_readable(void) { size_t len = 128; _z_wbuf_t wbf = _z_wbuf_make(len, false); assert(_z_wbuf_capacity(&wbf) == len); printf("\n>>> WBuf => Writable and Readable\n"); size_t writable = _z_wbuf_space_left(&wbf); printf(" Writable: %zu\n", writable); assert(writable == len); size_t readable = _z_wbuf_len(&wbf); printf(" Readable: %zu\n", readable); assert(readable == 0); size_t written = 0; while (written < len) { size_t to_write = 1 + gen_size_t() % (len - written); for (size_t i = 0; i < to_write; i++) { _z_wbuf_write(&wbf, 0); } written = written + to_write; writable = _z_wbuf_space_left(&wbf); printf(" Writable: %zu\n", writable); assert(writable == len - written); readable = _z_wbuf_len(&wbf); printf(" Readable: %zu\n", readable); assert(readable == written); } _z_wbuf_clear(&wbf); } void wbuf_write_zbuf_read(void) { size_t len = 128; _z_wbuf_t wbf = gen_wbuf(len); printf("\n>>> WBuf => Write and Read\n"); print_wbuf_overview(&wbf); printf(" Writing %zu bytes\n", len); for (size_t i = 0; i < len; i++) _z_wbuf_write(&wbf, (uint8_t)i % 255); printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); printf(" Written: %zu, Readable: %zu\n", len, _z_wbuf_len(&wbf)); assert(_z_wbuf_len(&wbf) == len); _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); assert(_z_zbuf_len(&zbf) == len); printf(" Reading %zu bytes\n", len); printf(" ["); for (uint8_t i = 0; i < len; i++) { uint8_t l = (uint8_t)i % 255; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf("]\n"); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void wbuf_write_zbuf_read_bytes(void) { size_t len = 128; _z_wbuf_t wbf = gen_wbuf(len); printf("\n>>> WBuf => Write and Read bytes\n"); print_wbuf_overview(&wbf); printf(" Writing %zu bytes\n", len); uint8_t *buf01 = (uint8_t *)z_malloc(len); for (size_t i = 0; i < len; i++) { buf01[i] = (uint8_t)i % 255; } _z_wbuf_write_bytes(&wbf, buf01, 0, len); printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); printf(" Written: %zu, Readable: %zu\n", len, _z_wbuf_len(&wbf)); assert(_z_wbuf_len(&wbf) == len); _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); printf(" ["); for (size_t i = 0; i < len; i++) { uint8_t l = buf01[i]; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf(" ]\n"); z_free(buf01); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void wbuf_put_zbuf_get(void) { size_t len = 128; _z_wbuf_t wbf = gen_wbuf(len); printf("\n>>> WBuf => Put and Get\n"); print_wbuf_overview(&wbf); // Initialize to 0 for (size_t i = 0; i < len; i++) { _z_wbuf_write(&wbf, 0); } printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); printf(" Putting %zu bytes\n", len); // Put data for (size_t i = 0; i < len; i++) { _z_wbuf_put(&wbf, (uint8_t)i % 255, i); } _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); printf(" Getting %zu bytes\n", len); printf(" ["); for (uint8_t i = 0; i < len; i++) { uint8_t l = (uint8_t)i % 255; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf(" ]\n"); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void wbuf_reusable_write_zbuf_read(void) { _z_wbuf_t wbf = gen_wbuf(128); for (int i = 1; i <= 10; i++) { size_t len = z_random_u8() % 128; printf("\n>>> WBuf => Write and Read round %d\n", i); print_wbuf_overview(&wbf); printf(" Writing %zu bytes\n", len); for (size_t z = 0; z < len; z++) { size_t prev_len = _z_wbuf_len(&wbf); assert(_z_wbuf_write(&wbf, (uint8_t)(z % 255)) == 0); assert(_z_wbuf_len(&wbf) == prev_len + 1); } printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); printf(" Written: %zu, Readable: %zu\n", len, _z_wbuf_len(&wbf)); assert(_z_wbuf_len(&wbf) == len); _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); assert(_z_zbuf_len(&zbf) == len); printf(" Reading %zu bytes\n", len); printf(" ["); for (uint8_t j = 0; j < len; j++) { uint8_t l = j % (uint8_t)255; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf("]\n"); _z_zbuf_clear(&zbf); _z_wbuf_reset(&wbf); } _z_wbuf_clear(&wbf); } void wbuf_set_pos_wbuf_get_pos(void) { size_t len = 128; _z_wbuf_t wbf = gen_wbuf(len); printf("\n>>> WBuf => SetPos and GetPos\n"); print_wbuf_overview(&wbf); // Initialize to 0 for (size_t i = 0; i < len; i++) { _z_wbuf_write(&wbf, 0); } assert(_z_wbuf_get_rpos(&wbf) == 0); assert(_z_wbuf_get_wpos(&wbf) == len); printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); for (size_t i = 0; i < 10; i++) { _z_wbuf_reset(&wbf); assert(_z_wbuf_get_rpos(&wbf) == 0); assert(_z_wbuf_get_wpos(&wbf) == 0); size_t lw_pos = gen_size_t() % (len + 1); printf(" Setting WPos: %zu\n", lw_pos); _z_wbuf_set_wpos(&wbf, lw_pos); size_t rw_pos = _z_wbuf_get_wpos(&wbf); printf(" Getting WPos: %zu\n", rw_pos); assert(lw_pos == rw_pos); size_t lr_pos = gen_size_t() % (lw_pos + 1); printf(" Setting RPos: %zu\n", lr_pos); _z_wbuf_set_rpos(&wbf, lr_pos); size_t rr_pos = _z_wbuf_get_rpos(&wbf); printf(" Getting RPos: %zu\n", rr_pos); assert(lr_pos == rr_pos); } _z_wbuf_clear(&wbf); } void wbuf_add_iosli(void) { uint8_t len = 16; _z_wbuf_t wbf = _z_wbuf_make(len, true); assert(_z_wbuf_capacity(&wbf) == len); printf("\n>>> WBuf => Add IOSli\n"); print_wbuf_overview(&wbf); uint8_t written = 0; uint8_t counter = 0; while (written < 255) { uint8_t remaining = 255 - written; uint8_t range = remaining < len ? remaining : len; uint8_t to_write = 1 + gen_uint8() % range; printf(" Writing %u bytes\n", to_write); for (uint8_t i = 0; i < to_write; i++) { _z_wbuf_write(&wbf, counter); counter++; } written = written + to_write; } printf(" IOSlices: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(&wbf), wbf._r_idx, wbf._w_idx); for (size_t i = 0; i < _z_wbuf_len_iosli(&wbf); i++) { _z_iosli_t *ios = _z_wbuf_get_iosli(&wbf, i); printf(" Idx: %zu => ", i); print_iosli(ios); printf("\n"); } _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); printf(" ["); for (uint8_t i = 0; i < counter; i++) { uint8_t l = i; uint8_t r = _z_zbuf_read(&zbf); printf(" %02x:%02x", l, r); assert(l == r); } printf(" ]\n"); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } #define VAL_SIZE 20 #define PAYLOAD_SIZE 32 void test_wbuf_wrap_bytes(void) { printf("Testing wbuf_wrap_bytes... "); // Emulate eclipse-zenoh/zenoh-pico issue #979 uint8_t val[VAL_SIZE]; memset(val, 0xaa, sizeof(val)); uint8_t payload[PAYLOAD_SIZE]; memset(payload, 0x55, sizeof(payload)); uint16_t payload_size = _ZP_ARRAY_SIZE(payload); _z_wbuf_t wbf = _z_wbuf_make(PAYLOAD_SIZE, true); assert(_z_wbuf_capacity(&wbf) == PAYLOAD_SIZE); // Write header _z_wbuf_write_bytes(&wbf, val, 0, sizeof(val)); // Write attachment _z_wbuf_write_bytes(&wbf, (uint8_t *)&payload_size, 0, sizeof(payload_size)); _z_wbuf_wrap_bytes(&wbf, payload, 0, sizeof(payload)); // Write payload _z_wbuf_write_bytes(&wbf, (uint8_t *)&payload_size, 0, sizeof(payload_size)); _z_wbuf_wrap_bytes(&wbf, payload, 0, sizeof(payload)); assert(_z_iosli_svec_len(&wbf._ioss) == 5); // Check header + attachment size _z_iosli_t *ios = _z_wbuf_get_iosli(&wbf, 0); assert(_z_iosli_readable(ios) == VAL_SIZE + sizeof(payload_size)); for (size_t i = 0; i < _z_iosli_readable(ios) - sizeof(payload_size); i++) { assert(ios->_buf[i] == 0xaa); } assert(ios->_buf[_z_iosli_readable(ios) - 2] == (payload_size & 0xff)); assert(ios->_buf[_z_iosli_readable(ios) - 1] == (payload_size >> 8)); // Check attachment ios = _z_wbuf_get_iosli(&wbf, 1); assert(_z_iosli_readable(ios) == PAYLOAD_SIZE); for (size_t i = 0; i < _z_iosli_readable(ios); i++) { assert(ios->_buf[i] == 0x55); } // Check payload size ios = _z_wbuf_get_iosli(&wbf, 2); assert(_z_iosli_readable(ios) == sizeof(payload_size)); assert(ios->_buf[0] == (payload_size & 0xff)); assert(ios->_buf[1] == (payload_size >> 8)); // Check payload ios = _z_wbuf_get_iosli(&wbf, 3); assert(_z_iosli_readable(ios) == PAYLOAD_SIZE); for (size_t i = 0; i < _z_iosli_readable(ios); i++) { assert(ios->_buf[i] == 0x55); } _z_wbuf_clear(&wbf); printf("Ok\n"); } /*=============================*/ /* Main */ /*=============================*/ int main(void) { for (unsigned int i = 0; i < RUNS; i++) { printf("\n\n== RUN %u\n", i); // IOsli iosli_writable_readable(); // ZBuf zbuf_writable_readable(); zbuf_compact(); zbuf_view(); // WBuf wbuf_writable_readable(); wbuf_set_pos_wbuf_get_pos(); wbuf_add_iosli(); // WBuf and ZBuf wbuf_write_zbuf_read(); wbuf_write_zbuf_read_bytes(); wbuf_put_zbuf_get(); // Reusable WBuf wbuf_reusable_write_zbuf_read(); } test_wbuf_wrap_bytes(); } ================================================ FILE: tests/z_json_encoder_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/utils/json_encoder.h" #include "zenoh-pico/utils/result.h" #undef NDEBUG #include #if Z_FEATURE_ADMIN_SPACE == 1 static void assert_bytes_eq_buf(const z_owned_bytes_t *b, const uint8_t *expected, size_t expected_len) { assert(b != NULL); assert(expected != NULL); assert(z_bytes_len(z_bytes_loan(b)) == expected_len); uint8_t *buf = (uint8_t *)z_malloc(expected_len); assert(buf != NULL); size_t n = _z_bytes_to_buf(&b->_val, buf, expected_len); assert(n == expected_len); assert(memcmp(buf, expected, expected_len) == 0); z_free(buf); } #define ASSERT_BYTES_EQ_LIT(bytes_ptr, lit) assert_bytes_eq_buf((bytes_ptr), (const uint8_t *)(lit), sizeof(lit) - 1) static void test_finish_requires_done(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; // Nothing written => finish must fail (validate checks !done) assert(_z_json_encoder_finish(&je, &out) == _Z_ERR_INVALID); _z_json_encoder_clear(&je); } static void test_empty_object(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); assert(_z_json_encoder_end_object(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out, "{}"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_simple_object_string(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); assert(_z_json_encoder_write_key(&je, "k") == _Z_RES_OK); assert(_z_json_encoder_write_string(&je, "v") == _Z_RES_OK); assert(_z_json_encoder_end_object(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out, "{\"k\":\"v\"}"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_array_two_values(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_array(&je) == _Z_RES_OK); assert(_z_json_encoder_write_string(&je, "a") == _Z_RES_OK); assert(_z_json_encoder_write_string(&je, "b") == _Z_RES_OK); assert(_z_json_encoder_end_array(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out, "[\"a\",\"b\"]"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_nested_object_array_numbers(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); assert(_z_json_encoder_write_key(&je, "a") == _Z_RES_OK); assert(_z_json_encoder_start_array(&je) == _Z_RES_OK); assert(_z_json_encoder_write_i64(&je, -1) == _Z_RES_OK); assert(_z_json_encoder_write_u64(&je, 2) == _Z_RES_OK); assert(_z_json_encoder_end_array(&je) == _Z_RES_OK); assert(_z_json_encoder_end_object(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out, "{\"a\":[-1,2]}"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_boolean_null_double(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_array(&je) == _Z_RES_OK); assert(_z_json_encoder_write_boolean(&je, true) == _Z_RES_OK); assert(_z_json_encoder_write_boolean(&je, false) == _Z_RES_OK); assert(_z_json_encoder_write_null(&je) == _Z_RES_OK); assert(_z_json_encoder_write_double(&je, 1.25) == _Z_RES_OK); assert(_z_json_encoder_end_array(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); // write_double uses "%.17g" => 1.25 ASSERT_BYTES_EQ_LIT(&out, "[true,false,null,1.25]"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_string_escaping_key_and_value(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out; assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); // key contains quote and backslash assert(_z_json_encoder_write_key(&je, "k\"\\") == _Z_RES_OK); // value contains newline and tab assert(_z_json_encoder_write_string(&je, "line1\n\tline2") == _Z_RES_OK); assert(_z_json_encoder_end_object(&je) == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out, "{\"k\\\"\\\\\":\"line1\\n\\tline2\"}"); z_bytes_drop(z_bytes_move(&out)); _z_json_encoder_clear(&je); } static void test_invalid_write_key_outside_object(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); assert(_z_json_encoder_write_key(&je, "k") == _Z_ERR_INVALID); _z_json_encoder_clear(&je); } static void test_invalid_value_in_object_without_key(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); // In object, value not allowed until key written assert(_z_json_encoder_write_string(&je, "v") == _Z_ERR_INVALID); _z_json_encoder_clear(&je); } static void test_invalid_end_object_when_expect_value(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); assert(_z_json_encoder_start_object(&je) == _Z_RES_OK); assert(_z_json_encoder_write_key(&je, "k") == _Z_RES_OK); // Expecting value => cannot end object assert(_z_json_encoder_end_object(&je) == _Z_ERR_INVALID); _z_json_encoder_clear(&je); } static void test_depth_overflow(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); for (size_t i = 0; i < Z_JSON_MAX_DEPTH; i++) { assert(_z_json_encoder_start_array(&je) == _Z_RES_OK); } assert(_z_json_encoder_start_array(&je) == _Z_ERR_OVERFLOW); _z_json_encoder_clear(&je); } static void test_finish_resets_encoder_state(void) { _z_json_encoder_t je; assert(_z_json_encoder_empty(&je) == _Z_RES_OK); z_owned_bytes_t out1; z_owned_bytes_t out2; assert(_z_json_encoder_write_string(&je, "one") == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out1) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out1, "\"one\""); z_bytes_drop(z_bytes_move(&out1)); // After finish, encoder should allow a new top-level value assert(_z_json_encoder_write_string(&je, "two") == _Z_RES_OK); assert(_z_json_encoder_finish(&je, &out2) == _Z_RES_OK); ASSERT_BYTES_EQ_LIT(&out2, "\"two\""); z_bytes_drop(z_bytes_move(&out2)); _z_json_encoder_clear(&je); } int main(void) { test_finish_requires_done(); test_empty_object(); test_simple_object_string(); test_array_two_values(); test_nested_object_array_numbers(); test_boolean_null_double(); test_string_escaping_key_and_value(); test_invalid_write_key_outside_object(); test_invalid_value_in_object_without_key(); test_invalid_end_object_when_expect_value(); test_depth_overflow(); test_finish_resets_encoder_state(); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf("Missing config token to build this test. This test requires: Z_FEATURE_ADMIN_SPACE\n"); return 0; } #endif // Z_FEATURE_ADMIN_SPACE == 1 ================================================ FILE: tests/z_keyexpr_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, #include #include #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/session/keyexpr.h" #undef NDEBUG #include #define TEST_TRUE_INTERSECT(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(_z_keyexpr_intersects(&ke_a, &ke_b)); #define TEST_FALSE_INTERSECT(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(!_z_keyexpr_intersects(&ke_a, &ke_b)); #define TEST_TRUE_INCLUDE(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(_z_keyexpr_includes(&ke_a, &ke_b)); #define TEST_FALSE_INCLUDE(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(!_z_keyexpr_includes(&ke_a, &ke_b)); #define TEST_TRUE_EQUAL(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(_z_keyexpr_equals(&ke_a, &ke_b)); #define TEST_FALSE_EQUAL(a, b) \ ke_a = _z_keyexpr_alias_from_str(a); \ ke_b = _z_keyexpr_alias_from_str(b); \ assert(!_z_keyexpr_equals(&ke_a, &ke_b)); void test_intersects(void) { _z_keyexpr_t ke_a, ke_b; TEST_TRUE_INTERSECT("a", "a") TEST_TRUE_INTERSECT("a/b", "a/b") TEST_TRUE_INTERSECT("*", "abc") TEST_TRUE_INTERSECT("*", "abc") TEST_TRUE_INTERSECT("*", "abc") TEST_TRUE_INTERSECT("*", "xxx") TEST_TRUE_INTERSECT("ab$*", "abcd") TEST_TRUE_INTERSECT("ab$*d", "abcd") TEST_TRUE_INTERSECT("ab$*", "ab") TEST_FALSE_INTERSECT("ab/*", "ab") TEST_TRUE_INTERSECT("a/*/c/*/e", "a/b/c/d/e") TEST_TRUE_INTERSECT("a/**/d/**/l", "a/b/c/d/e/f/g/h/i/l") TEST_TRUE_INTERSECT("a/**/d/**/l", "a/d/foo/l") TEST_TRUE_INTERSECT("a/$*b/c/$*d/e", "a/xb/c/xd/e") TEST_FALSE_INTERSECT("a/*/c/*/e", "a/c/e") TEST_FALSE_INTERSECT("a/*/c/*/e", "a/b/c/d/x/e") TEST_FALSE_INTERSECT("ab$*cd", "abxxcxxd") TEST_TRUE_INTERSECT("ab$*cd", "abxxcxxcd") TEST_FALSE_INTERSECT("ab$*cd", "abxxcxxcdx") TEST_TRUE_INTERSECT("**", "abc") TEST_TRUE_INTERSECT("**", "a/b/c") TEST_TRUE_INTERSECT("ab/**", "ab") TEST_TRUE_INTERSECT("**/xyz", "a/b/xyz/d/e/f/xyz") TEST_FALSE_INTERSECT("**/xyz$*xyz", "a/b/xyz/d/e/f/xyz") TEST_TRUE_INTERSECT("a/**/c/**/e", "a/b/b/b/c/d/d/d/e") TEST_TRUE_INTERSECT("a/**/c/**/e", "a/c/e") TEST_TRUE_INTERSECT("a/**/c/*/e/*", "a/b/b/b/c/d/d/c/d/e/f") TEST_FALSE_INTERSECT("a/**/c/*/e/*", "a/b/b/b/c/d/d/c/d/d/e/f") TEST_FALSE_INTERSECT("ab$*cd", "abxxcxxcdx") TEST_TRUE_INTERSECT("x/abc", "x/abc") TEST_FALSE_INTERSECT("x/abc", "abc") TEST_TRUE_INTERSECT("x/*", "x/abc") TEST_FALSE_INTERSECT("x/*", "abc") TEST_FALSE_INTERSECT("*", "x/abc") TEST_TRUE_INTERSECT("x/*", "x/abc$*") TEST_TRUE_INTERSECT("x/$*abc", "x/abc$*") TEST_TRUE_INTERSECT("x/a$*", "x/abc$*") TEST_TRUE_INTERSECT("x/a$*de", "x/abc$*de") TEST_TRUE_INTERSECT("x/a$*d$*e", "x/a$*e") TEST_TRUE_INTERSECT("x/a$*d$*e", "x/a$*c$*e") TEST_TRUE_INTERSECT("x/a$*d$*e", "x/ade") TEST_FALSE_INTERSECT("x/c$*", "x/abc$*") TEST_FALSE_INTERSECT("x/$*d", "x/$*e") TEST_TRUE_INTERSECT("a", "a") TEST_TRUE_INTERSECT("a/b", "a/b") TEST_TRUE_INTERSECT("*", "a") TEST_TRUE_INTERSECT("a", "*") TEST_TRUE_INTERSECT("*", "aaaaa") TEST_TRUE_INTERSECT("**", "a") TEST_TRUE_INTERSECT("a", "**") TEST_TRUE_INTERSECT("**", "a") TEST_TRUE_INTERSECT("a/a/a/a", "**") TEST_TRUE_INTERSECT("a/*", "a/b") TEST_FALSE_INTERSECT("a/*/b", "a/b") TEST_TRUE_INTERSECT("a/**/b", "a/b") TEST_TRUE_INTERSECT("a/b$*", "a/b") TEST_TRUE_INTERSECT("a/$*b$*", "a/b") TEST_TRUE_INTERSECT("a/$*b", "a/b") TEST_TRUE_INTERSECT("a/b$*", "a/bc") TEST_TRUE_INTERSECT("a/$*b$*", "a/ebc") TEST_TRUE_INTERSECT("a/$*b", "a/cb") TEST_FALSE_INTERSECT("a/b$*", "a/ebc") TEST_FALSE_INTERSECT("a/$*b", "a/cbc") TEST_TRUE_INTERSECT("a/**/b$*", "a/b") TEST_TRUE_INTERSECT("a/**/$*b$*", "a/b") TEST_TRUE_INTERSECT("a/**/$*b", "a/b") TEST_TRUE_INTERSECT("a/**/b$*", "a/bc") TEST_TRUE_INTERSECT("a/**/$*b$*", "a/ebc") TEST_TRUE_INTERSECT("a/**/$*b", "a/cb") TEST_FALSE_INTERSECT("a/**/b$*", "a/ebc") TEST_FALSE_INTERSECT("a/**/$*b", "a/cbc") TEST_TRUE_INTERSECT("@a", "@a") TEST_FALSE_INTERSECT("@a", "@ab") TEST_FALSE_INTERSECT("@a", "@a/b") TEST_FALSE_INTERSECT("@a", "@a/*") TEST_FALSE_INTERSECT("@a", "@a/*/**") TEST_TRUE_INTERSECT("@a", "@a/**") TEST_FALSE_INTERSECT("**/xyz$*xyz", "@a/b/xyzdefxyz") TEST_TRUE_INTERSECT("@a/**/c/**/e", "@a/b/b/b/c/d/d/d/e") TEST_FALSE_INTERSECT("@a/**/c/**/e", "@a/@b/b/b/c/d/d/d/e") TEST_TRUE_INTERSECT("@a/**/@c/**/e", "@a/b/b/b/@c/d/d/d/e") TEST_TRUE_INTERSECT("@a/**/e", "@a/b/b/d/d/d/e") TEST_TRUE_INTERSECT("@a/**/e", "@a/b/b/b/d/d/d/e") TEST_TRUE_INTERSECT("@a/**/e", "@a/b/b/c/d/d/d/e") TEST_FALSE_INTERSECT("@a/**/e", "@a/b/b/@c/b/d/d/d/e") TEST_FALSE_INTERSECT("@a/*", "@a/@b") TEST_FALSE_INTERSECT("@a/**", "@a/@b") TEST_TRUE_INTERSECT("@a/**/@b", "@a/@b") TEST_FALSE_INTERSECT("@a/**/@b", "@a/**/@c/**/@b") TEST_TRUE_INTERSECT("@a/@b/**", "@a/@b") TEST_TRUE_INTERSECT("@a/**/@c/@b", "@a/**/@c/**/@b") TEST_TRUE_INTERSECT("@a/**/@c/**/@b", "@a/**/@c/@b") TEST_TRUE_INTERSECT("a", "$*a"); TEST_TRUE_INTERSECT("$*a", "a"); TEST_FALSE_INTERSECT("a/**/$*b", "a/@b") TEST_TRUE_INTERSECT("**/@a/b/c/**", "@a/b/c"); TEST_FALSE_INTERSECT("**/@a/b/c/**", "@b/b/c"); TEST_TRUE_INTERSECT("**/@a/@b/@c/**", "@a/@b/@c"); TEST_FALSE_INTERSECT("**/@a/@b/@c/**", "@a/@a/@c"); } void test_includes(void) { _z_keyexpr_t ke_a, ke_b; TEST_TRUE_INCLUDE("a", "a") TEST_TRUE_INCLUDE("a/b", "a/b") TEST_TRUE_INCLUDE("*", "a") TEST_FALSE_INCLUDE("a", "*") TEST_TRUE_INCLUDE("*", "aaaaa") TEST_TRUE_INCLUDE("**", "a") TEST_FALSE_INCLUDE("a", "**") TEST_TRUE_INCLUDE("**", "a") TEST_TRUE_INCLUDE("**", "a/a/a/a") TEST_TRUE_INCLUDE("**", "*/**") TEST_TRUE_INCLUDE("*/**", "*/**") TEST_FALSE_INCLUDE("*/**", "**") TEST_FALSE_INCLUDE("a/a/a/a", "**") TEST_TRUE_INCLUDE("a/*", "a/b") TEST_FALSE_INCLUDE("a/*/b", "a/b") TEST_TRUE_INCLUDE("a/**/b", "a/b") TEST_TRUE_INCLUDE("a/b$*", "a/b") TEST_FALSE_INCLUDE("a/b", "a/b$*") TEST_TRUE_INCLUDE("a/$*b$*", "a/b") TEST_TRUE_INCLUDE("a/$*b", "a/b") TEST_TRUE_INCLUDE("a/b$*", "a/bc") TEST_TRUE_INCLUDE("a/$*b$*", "a/ebc") TEST_TRUE_INCLUDE("a/$*b", "a/cb") TEST_FALSE_INCLUDE("a/b$*", "a/ebc") TEST_FALSE_INCLUDE("a/$*b", "a/cbc") TEST_TRUE_INCLUDE("a/**/b$*", "a/b") TEST_TRUE_INCLUDE("a/**/$*b$*", "a/b") TEST_TRUE_INCLUDE("a/**/$*b", "a/b") TEST_TRUE_INCLUDE("a/**/b$*", "a/bc") TEST_TRUE_INCLUDE("a/**/$*b$*", "a/ebc") TEST_TRUE_INCLUDE("a/**/$*b", "a/cb") TEST_FALSE_INCLUDE("a/**/b$*", "a/ebc") TEST_FALSE_INCLUDE("a/**/$*b", "a/cbc") TEST_TRUE_INCLUDE("@a", "@a") TEST_FALSE_INCLUDE("@a", "@ab") TEST_FALSE_INCLUDE("@a", "@a/b") TEST_FALSE_INCLUDE("@a", "@a/*") TEST_FALSE_INCLUDE("@a", "@a/*/**") TEST_FALSE_INCLUDE("@a", "@a/**") TEST_TRUE_INCLUDE("@a/**", "@a") TEST_FALSE_INCLUDE("**/xyz$*xyz", "@a/b/xyzdefxyz") TEST_TRUE_INCLUDE("@a/**/c/**/e", "@a/b/b/b/c/d/d/d/e") TEST_FALSE_INCLUDE("@a/*", "@a/@b") TEST_FALSE_INCLUDE("@a/**", "@a/@b") TEST_TRUE_INCLUDE("@a/**/@b", "@a/@b") TEST_TRUE_INCLUDE("@a/@b/**", "@a/@b") TEST_FALSE_INCLUDE("a/**/$*b", "a/@b") TEST_TRUE_INCLUDE("**/@a/b/c/**", "@a/b/c"); TEST_FALSE_INCLUDE("**/@a/b/c/**", "@b/b/c"); TEST_TRUE_INCLUDE("**/@a/@b/@c/**", "@a/@b/@c"); TEST_FALSE_INCLUDE("**/@a/@b/@c/**", "@a/@a/@c"); } void test_canonize(void) { // clang-format off #define N 31 const char *input[N] = {"greetings/hello/there", "greetings/good/*/morning", "greetings/*", "greetings/*/**", "greetings/$*", "greetings/**/*/morning", "greetings/**/*/g/morning", "greetings/**/*/m", "greetings/**/*", "greetings/**/**", "greetings/**/**/morning", "greetings/**/**/g/morning", "greetings/**/**/m", "greetings/**/*/**", "$*", "$*$*", "$*$*$*", "$*hi$*$*", "$*$*hi$*", "hi$*$*$*", "$*$*$*hi", "$*$*$*hi$*$*$*", "hi*", "/hi", "hi/", "", "greetings/**/*/", "greetings/**/*/e?", "greetings/**/*/e#", "greetings/**/*/e$", "greetings/**/*/$e"}; const zp_keyexpr_canon_status_t expected[N] = {Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_SUCCESS, Z_KEYEXPR_CANON_STARS_IN_CHUNK, Z_KEYEXPR_CANON_EMPTY_CHUNK, Z_KEYEXPR_CANON_EMPTY_CHUNK, Z_KEYEXPR_CANON_EMPTY_CHUNK, Z_KEYEXPR_CANON_EMPTY_CHUNK, Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK, Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK, Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR, Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR}; const char *canonized[N] = {"greetings/hello/there", "greetings/good/*/morning", "greetings/*", "greetings/*/**", "greetings/*", "greetings/*/**/morning", "greetings/*/**/g/morning", "greetings/*/**/m", "greetings/*/**", "greetings/**", "greetings/**/morning", "greetings/**/g/morning", "greetings/**/m", "greetings/*/**", "*", "*", "*", "$*hi$*", "$*hi$*", "hi$*", "$*hi", "$*hi$*", "hi*", "/hi", "hi/", "", "greetings/**/*/", "greetings/**/*/e?", "greetings/**/*/e#", "greetings/**/*/e$", "greetings/**/*/$e"}; // clang-format on for (int i = 0; i < N; i++) { const char *ke = input[i]; char *canon = (char *)malloc(128); memset(canon, 0, 128); strncpy(canon, ke, 128); size_t canon_len = strlen(canon); zp_keyexpr_canon_status_t status = z_keyexpr_canonize(canon, &canon_len); printf("%s ", ke); printf(" Status: %d : %d\n", status, expected[i]); assert(status == expected[i]); if (status == Z_KEYEXPR_CANON_SUCCESS) { printf(" Match: %.*s : %s\n", (int)canon_len, canon, canonized[i]); assert(strncmp(canonized[i], canon, canon_len) == 0); } free(canon); } for (int i = 0; i < N; i++) { const char *ke = input[i]; char *canon = (char *)malloc(128); memset(canon, 0, 128); strncpy(canon, ke, 128); size_t canon_len = strlen(canon); zp_keyexpr_canon_status_t status = z_keyexpr_canonize(canon, &canon_len); printf("%s ", ke); printf(" Status: %d : %d", status, expected[i]); assert(status == expected[i]); if (status == Z_KEYEXPR_CANON_SUCCESS) { printf(" Match: %.*s : %s", (int)canon_len, canon, canonized[i]); assert(strncmp(canonized[i], canon, canon_len) == 0); } printf("\n"); free(canon); } } void test_equals(void) { _z_keyexpr_t ke_a, ke_b; TEST_FALSE_EQUAL("a/**/$*b", "a/cb"); TEST_FALSE_EQUAL("a/bc", "a/cb"); TEST_TRUE_EQUAL("greetings/hello/there", "greetings/hello/there"); } bool keyexpr_equals_string(const z_loaned_keyexpr_t *ke, const char *s) { z_view_string_t vs; z_keyexpr_as_view_string(ke, &vs); _z_string_t str = _z_string_alias_str(s); return _z_string_equals(z_view_string_loan(&vs), &str); } void test_keyexpr_constructor(void) { z_owned_keyexpr_t ke; z_keyexpr_from_str(&ke, "a/b/c"); assert(keyexpr_equals_string(z_keyexpr_loan(&ke), "a/b/c")); z_keyexpr_drop(z_keyexpr_move(&ke)); z_keyexpr_from_substr(&ke, "a/b/c/d/e", 5); assert(keyexpr_equals_string(z_keyexpr_loan(&ke), "a/b/c")); z_keyexpr_drop(z_keyexpr_move(&ke)); assert(0 == z_keyexpr_from_str_autocanonize(&ke, "a/**/**")); assert(keyexpr_equals_string(z_keyexpr_loan(&ke), "a/**")); z_keyexpr_drop(z_keyexpr_move(&ke)); size_t len = 9; assert(0 == z_keyexpr_from_substr_autocanonize(&ke, "a/**/**/m/b/c", &len)); assert(keyexpr_equals_string(z_keyexpr_loan(&ke), "a/**/m")); assert(len == 6); z_keyexpr_drop(z_keyexpr_move(&ke)); z_view_keyexpr_t vke; z_view_keyexpr_from_str(&vke, "a/b/c"); assert(keyexpr_equals_string(z_view_keyexpr_loan(&vke), "a/b/c")); char s[] = "a/**/**"; z_view_keyexpr_from_str_autocanonize(&vke, s); assert(keyexpr_equals_string(z_view_keyexpr_loan(&vke), "a/**")); } void test_concat(void) { z_owned_keyexpr_t ke1, ke2; z_keyexpr_from_str(&ke1, "a/b/c/*"); assert(0 == z_keyexpr_concat(&ke2, z_keyexpr_loan(&ke1), "/d/e/*", 4)); assert(keyexpr_equals_string(z_keyexpr_loan(&ke2), "a/b/c/*/d/e")); z_keyexpr_drop(z_keyexpr_move(&ke2)); assert(0 != z_keyexpr_concat(&ke2, z_keyexpr_loan(&ke1), "*/e/*", 3)); assert(!z_internal_keyexpr_check(&ke2)); z_keyexpr_drop(z_keyexpr_move(&ke1)); } void test_join(void) { z_owned_keyexpr_t ke1, ke2, ke3; z_keyexpr_from_str(&ke1, "a/b/c/*"); z_keyexpr_from_str(&ke2, "d/e/*"); assert(0 == z_keyexpr_join(&ke3, z_keyexpr_loan(&ke1), z_keyexpr_loan(&ke2))); assert(keyexpr_equals_string(z_keyexpr_loan(&ke3), "a/b/c/*/d/e/*")); z_keyexpr_drop(z_keyexpr_move(&ke1)); z_keyexpr_drop(z_keyexpr_move(&ke2)); z_keyexpr_drop(z_keyexpr_move(&ke3)); z_keyexpr_from_str(&ke1, "a/*/**"); z_keyexpr_from_str(&ke2, "**/d/e/c"); assert(0 == z_keyexpr_join(&ke3, z_keyexpr_loan(&ke1), z_keyexpr_loan(&ke2))); assert(keyexpr_equals_string(z_keyexpr_loan(&ke3), "a/*/**/d/e/c")); z_keyexpr_drop(z_keyexpr_move(&ke1)); z_keyexpr_drop(z_keyexpr_move(&ke2)); z_keyexpr_drop(z_keyexpr_move(&ke3)); } void test_relation_to(void) { z_view_keyexpr_t foobar, foostar, barstar; z_view_keyexpr_from_str(&foobar, "foo/bar"); z_view_keyexpr_from_str(&foostar, "foo/*"); z_view_keyexpr_from_str(&barstar, "bar/*"); assert(z_keyexpr_relation_to(z_view_keyexpr_loan(&foostar), z_view_keyexpr_loan(&foobar)) == Z_KEYEXPR_INTERSECTION_LEVEL_INCLUDES); assert(z_keyexpr_relation_to(z_view_keyexpr_loan(&foobar), z_view_keyexpr_loan(&foostar)) == Z_KEYEXPR_INTERSECTION_LEVEL_INTERSECTS); assert(z_keyexpr_relation_to(z_view_keyexpr_loan(&foostar), z_view_keyexpr_loan(&foostar)) == Z_KEYEXPR_INTERSECTION_LEVEL_EQUALS); assert(z_keyexpr_relation_to(z_view_keyexpr_loan(&barstar), z_view_keyexpr_loan(&foobar)) == Z_KEYEXPR_INTERSECTION_LEVEL_DISJOINT); } void test_non_wild_prefix_len(void) { _z_keyexpr_t ke1, ke2, ke3, ke4, ke5; ke1 = _z_keyexpr_alias_from_str("foo/bar/**"); ke2 = _z_keyexpr_alias_from_str("foo/*/baz"); ke3 = _z_keyexpr_alias_from_str("bar1/ab$*/baz"); ke4 = _z_keyexpr_alias_from_str("foo/bar"); ke5 = _z_keyexpr_alias_from_str("**"); assert(_z_keyexpr_non_wild_prefix_len(&ke1) == 7); assert(_z_keyexpr_non_wild_prefix_len(&ke2) == 3); assert(_z_keyexpr_non_wild_prefix_len(&ke3) == 4); assert(_z_keyexpr_non_wild_prefix_len(&ke4) == 7); assert(_z_keyexpr_non_wild_prefix_len(&ke5) == 0); } int main(void) { test_intersects(); test_includes(); test_canonize(); test_equals(); test_keyexpr_constructor(); test_concat(); test_join(); test_relation_to(); test_non_wild_prefix_len(); return 0; } ================================================ FILE: tests/z_local_loopback_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #undef NDEBUG #include #include #include #include #include #include "zenoh-pico/api/constants.h" #include "zenoh-pico/api/macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/bytes.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/net/primitives.h" #include "zenoh-pico/net/reply.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/session/keyexpr.h" #include "zenoh-pico/session/loopback.h" #include "zenoh-pico/session/query.h" #include "zenoh-pico/session/queryable.h" #include "zenoh-pico/session/resource.h" #include "zenoh-pico/session/session.h" #include "zenoh-pico/session/subscription.h" #include "zenoh-pico/session/utils.h" #include "zenoh-pico/transport/transport.h" #include "zenoh-pico/utils/result.h" #if (Z_FEATURE_SUBSCRIPTION == 1) && (Z_FEATURE_QUERYABLE == 1) && (Z_FEATURE_QUERY == 1) && \ (Z_FEATURE_LOCAL_SUBSCRIBER == 1) && (Z_FEATURE_LOCAL_QUERYABLE == 1) static atomic_uint g_network_send_count = 0; static atomic_uint g_network_final_send_count = 0; static atomic_uint g_local_put_delivery_count = 0; static atomic_uint g_local_query_delivery_count = 0; static atomic_uint g_query_drop_callback_count = 0; static atomic_uint g_query_reply_callback_count = 0; static _z_session_t g_session; static _z_session_rc_t g_session_rc = {0}; static _z_transport_common_t g_fake_transport = {0}; static bool g_transport_ready = false; static _z_link_t g_dummy_link = {0}; static _z_transport_common_t *loopback_override(_z_session_t *zn) { if (g_transport_ready && zn == &g_session) { return &g_fake_transport; } return NULL; } static z_result_t z_send_override(_z_session_t *zn, const _z_network_message_t *n_msg, z_reliability_t reliability, z_congestion_control_t cong_ctrl, void *peer, bool *handled) { _ZP_UNUSED(zn); _ZP_UNUSED(reliability); _ZP_UNUSED(cong_ctrl); _ZP_UNUSED(peer); if (n_msg != NULL) { atomic_fetch_add_explicit(&g_network_send_count, 1, memory_order_relaxed); if (n_msg->_tag == _Z_N_RESPONSE_FINAL) { atomic_fetch_add_explicit(&g_network_final_send_count, 1, memory_order_relaxed); } } if (handled != NULL) { *handled = true; } return _Z_RES_OK; } static void local_sample_callback(_z_sample_t *sample, void *arg) { _ZP_UNUSED(sample); atomic_fetch_add_explicit((atomic_uint *)arg, 1, memory_order_relaxed); } static void local_query_callback(_z_query_rc_t *query_rc, void *arg) { atomic_fetch_add_explicit((atomic_uint *)arg, 1, memory_order_relaxed); const char data[] = "loopback-response"; _z_bytes_t payload = _z_bytes_null(); assert(_z_bytes_from_buf(&payload, (const uint8_t *)data, sizeof(data) - 1) == _Z_RES_OK); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t timestamp = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); _z_network_message_t msg; _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&_Z_RC_IN_VAL(query_rc)->_key, &g_session); _z_n_msg_make_reply_ok_put(&msg, &g_session._local_zid, _Z_RC_IN_VAL(query_rc)->_request_id, &wireexpr, Z_RELIABILITY_DEFAULT, Z_CONSOLIDATION_MODE_DEFAULT, qos, ×tamp, &source_info, &payload, &encoding, NULL); assert(_z_handle_network_message(&g_fake_transport, &msg, NULL) == _Z_RES_OK); } static void query_reply_callback(_z_reply_t *reply, void *arg) { _ZP_UNUSED(reply); _ZP_UNUSED(arg); atomic_fetch_add_explicit(&g_query_reply_callback_count, 1, memory_order_relaxed); } static void query_dropper(void *arg) { _ZP_UNUSED(arg); atomic_fetch_add_explicit(&g_query_drop_callback_count, 1, memory_order_relaxed); } static void add_fake_peer(void) { // Add a fake peer to simulate a remote connection g_session._tp._transport._unicast._peers = _z_transport_peer_unicast_slist_push_empty(g_session._tp._transport._unicast._peers); _z_transport_peer_unicast_t *peer = _z_transport_peer_unicast_slist_value(g_session._tp._transport._unicast._peers); *peer = (_z_transport_peer_unicast_t){0}; g_session._tp._transport._unicast._common._link = &g_dummy_link; } static void setup_session(void) { _z_id_t zid; _z_session_generate_zid(&zid, Z_ZID_LENGTH); assert(_z_session_init(&g_session, &zid) == _Z_RES_OK); if (_Z_RC_IS_NULL(&g_session_rc)) { g_session_rc = _z_session_rc_new(&g_session); assert(!_Z_RC_IS_NULL(&g_session_rc)); } else { g_session_rc._val = &g_session; } g_dummy_link = (_z_link_t){0}; g_fake_transport = (_z_transport_common_t){0}; g_fake_transport._session = _z_session_rc_clone_as_weak(&g_session_rc); g_fake_transport._link = &g_dummy_link; g_transport_ready = true; _z_session_set_transport_common_override(loopback_override); _z_transport_set_send_n_msg_override(z_send_override); g_session._tp._type = _Z_TRANSPORT_UNICAST_TYPE; } static void cleanup_session(void) { if (g_transport_ready) { g_transport_ready = false; } _z_transport_peer_unicast_slist_free(&g_session._tp._transport._unicast._peers); g_session._tp._transport._unicast._peers = NULL; g_session._tp._transport._unicast._common._link = NULL; g_session._tp._type = _Z_TRANSPORT_NONE; _z_session_clear(&g_session); _z_session_set_transport_common_override(NULL); _z_transport_set_send_n_msg_override(NULL); } static _z_declared_keyexpr_t create_local_resource(const char *key_str) { _z_declared_keyexpr_t ke = _z_declared_keyexpr_alias_from_str(key_str); _z_declared_keyexpr_t keyexpr; assert(_z_declared_keyexpr_declare(&g_session_rc, &keyexpr, &ke) == _Z_RES_OK); return keyexpr; } static void cleanup_local_resource(_z_declared_keyexpr_t *keyexpr) { _z_declared_keyexpr_clear(keyexpr); } static _z_subscription_rc_t register_local_subscription(const _z_declared_keyexpr_t *keyexpr, atomic_uint *counter, z_locality_t allowed_origin) { _z_subscription_t sub_entry = {0}; sub_entry._id = _z_get_entity_id(&g_session); _z_declared_keyexpr_copy(&sub_entry._key, keyexpr); sub_entry._callback = local_sample_callback; sub_entry._dropper = NULL; sub_entry._arg = counter; sub_entry._allowed_origin = allowed_origin; _z_subscription_rc_t subscription_rc = _z_register_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub_entry); assert(!_Z_RC_IS_NULL(&subscription_rc)); return subscription_rc; } static _z_session_queryable_rc_t register_local_queryable(const _z_declared_keyexpr_t *keyexpr, atomic_uint *counter, z_locality_t allowed_origin) { _z_session_queryable_t queryable_entry = {0}; _z_declared_keyexpr_copy(&queryable_entry._key, keyexpr); queryable_entry._callback = local_query_callback; queryable_entry._dropper = NULL; queryable_entry._arg = counter; queryable_entry._complete = false; queryable_entry._allowed_origin = allowed_origin; _z_session_queryable_rc_t queryable_rc = _z_register_session_queryable(&g_session, &queryable_entry); assert(!_Z_RC_IS_NULL(&queryable_rc)); return queryable_rc; } static void test_put_local_only_single(void) { setup_session(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put"); _z_subscription_rc_t subscription_rc = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; _z_bytes_t payload; assert(_z_bytes_from_buf(&payload, (const uint8_t *)payload_data, sizeof(payload_data) - 1) == _Z_RES_OK); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t ts = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); z_result_t delivered = _z_session_deliver_push_locally(&g_session, &keyexpr._inner, &payload, &encoding, Z_SAMPLE_KIND_PUT, qos, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info); assert(delivered == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); _z_bytes_drop(&payload); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &subscription_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_put_local_only_via_api(void) { setup_session(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/api"); _z_subscription_rc_t subscription_rc = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; z_owned_bytes_t payload = {0}; assert(z_bytes_from_buf(&payload, (uint8_t *)payload_data, sizeof(payload_data) - 1, NULL, NULL) == Z_OK); z_put_options_t opt; z_put_options_default(&opt); opt.allowed_destination = Z_LOCALITY_SESSION_LOCAL; z_result_t res = z_put(&g_session_rc, (const z_loaned_keyexpr_t *)&keyexpr, z_move(payload), &opt); assert(res == Z_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); z_owned_bytes_t payload2 = {0}; assert(z_bytes_from_buf(&payload2, (uint8_t *)payload_data, sizeof(payload_data) - 1, NULL, NULL) == Z_OK); z_put_options_default(&opt); opt.allowed_destination = Z_LOCALITY_ANY; res = z_put(&g_session_rc, (const z_loaned_keyexpr_t *)&keyexpr, z_move(payload2), &opt); assert(res == Z_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &subscription_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_put_local_and_remote_via_api(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/api-mixed"); _z_subscription_rc_t subscription_rc = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_ANY); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; z_owned_bytes_t payload = {0}; assert(z_bytes_from_buf(&payload, (uint8_t *)payload_data, sizeof(payload_data) - 1, NULL, NULL) == Z_OK); z_put_options_t opt; z_put_options_default(&opt); opt.allowed_destination = Z_LOCALITY_SESSION_LOCAL; z_result_t res = z_put(&g_session_rc, (const z_loaned_keyexpr_t *)&keyexpr, z_move(payload), &opt); assert(res == Z_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); z_owned_bytes_t payload2 = {0}; assert(z_bytes_from_buf(&payload2, (uint8_t *)payload_data, sizeof(payload_data) - 1, NULL, NULL) == Z_OK); opt.allowed_destination = Z_LOCALITY_ANY; res = z_put(&g_session_rc, (const z_loaned_keyexpr_t *)&keyexpr, z_move(payload2), &opt); assert(res == Z_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &subscription_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_query_local_only_single(void) { setup_session(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/query"); _z_session_queryable_rc_t queryable_rc = register_local_queryable(&keyexpr, &g_local_query_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_final_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); // With Z_CONSOLIDATION_MODE_DEFAULT should be set to LATEST, when no time is specified // Explicitly set LATEST consolidation mode for clarity assert(g_session._tp._transport._unicast._common._link == NULL); assert(_z_transport_peer_unicast_slist_is_empty(g_session._tp._transport._unicast._peers)); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); z_result_t res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_SESSION_LOCAL, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_final_send_count, memory_order_relaxed) == 0); assert(g_session._pending_queries == NULL); _z_unregister_session_queryable(&g_session, &queryable_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_put_local_only_multiple(void) { setup_session(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/multi"); _z_subscription_rc_t sub_primary = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_uint local_put_secondary_count = 0; _z_subscription_rc_t sub_secondary = register_local_subscription(&keyexpr, &local_put_secondary_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&local_put_secondary_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; _z_bytes_t payload; assert(_z_bytes_from_buf(&payload, (const uint8_t *)payload_data, sizeof(payload_data) - 1) == _Z_RES_OK); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t ts = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); z_result_t delivered = _z_session_deliver_push_locally(&g_session, &keyexpr._inner, &payload, &encoding, Z_SAMPLE_KIND_PUT, qos, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info); assert(delivered == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&local_put_secondary_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); _z_bytes_drop(&payload); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub_secondary); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub_primary); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_put_local_and_remote(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/mixed"); _z_subscription_rc_t sub_primary = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_ANY); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; _z_bytes_t payload; assert(_z_bytes_from_buf(&payload, (const uint8_t *)payload_data, sizeof(payload_data) - 1) == _Z_RES_OK); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t ts = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); // Session-local only delivery should not touch transport z_result_t res = _z_write(&g_session, &keyexpr, &payload, &encoding, Z_SAMPLE_KIND_PUT, Z_CONGESTION_CONTROL_BLOCK, Z_PRIORITY_DEFAULT, false, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info, Z_LOCALITY_SESSION_LOCAL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); // Allow remote recipients; send to transport in addition to local delivery atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); res = _z_write(&g_session, &keyexpr, &payload, &encoding, Z_SAMPLE_KIND_PUT, Z_CONGESTION_CONTROL_BLOCK, Z_PRIORITY_DEFAULT, false, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info, Z_LOCALITY_ANY); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); _z_bytes_drop(&payload); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub_primary); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_query_local_only_multiple(void) { setup_session(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/query/multi"); _z_session_queryable_rc_t queryable_primary = register_local_queryable(&keyexpr, &g_local_query_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_uint local_query_secondary_count = 0; _z_session_queryable_rc_t queryable_secondary = register_local_queryable(&keyexpr, &local_query_secondary_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_final_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&local_query_secondary_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); // Explicitly set LATEST consolidation mode for clarity (should be default) _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); z_result_t res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_SESSION_LOCAL, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&local_query_secondary_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_final_send_count, memory_order_relaxed) == 0); assert(g_session._pending_queries == NULL); _z_unregister_session_queryable(&g_session, &queryable_secondary); _z_unregister_session_queryable(&g_session, &queryable_primary); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_query_local_and_remote(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/query/mixed"); _z_session_queryable_rc_t queryable_primary = register_local_queryable(&keyexpr, &g_local_query_delivery_count, Z_LOCALITY_SESSION_LOCAL); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_final_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); // Explicitly set LATEST consolidation mode for clarity (should be default) // Locality limited to session, loopback-only, transport untouched _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); z_result_t res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_SESSION_LOCAL, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_final_send_count, memory_order_relaxed) == 0); assert(g_session._pending_queries == NULL); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_final_send_count, 0, memory_order_relaxed); // Permit remote delivery; still send to loopback, but network request must be emitted as well res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_ANY, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_network_final_send_count, memory_order_relaxed) == 0); assert(g_session._pending_queries != NULL); // Simulate REPLY from remote queryable _z_pending_query_t *pq = _z_pending_query_slist_value(g_session._pending_queries); _z_zint_t request_id = pq->_id; const char remote_data[] = "remote-response"; _z_bytes_t remote_payload = _z_bytes_null(); assert(_z_bytes_from_buf(&remote_payload, (const uint8_t *)remote_data, sizeof(remote_data) - 1) == _Z_RES_OK); _z_id_t remote_zid = _z_id_empty(); _z_session_generate_zid(&remote_zid, Z_ZID_LENGTH); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t timestamp = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); _z_network_message_t reply_msg; _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&keyexpr, &g_session); _z_n_msg_make_reply_ok_put(&reply_msg, &remote_zid, request_id, &wireexpr, Z_RELIABILITY_RELIABLE, Z_CONSOLIDATION_MODE_DEFAULT, qos, ×tamp, &source_info, &remote_payload, &encoding, NULL); res = _z_handle_network_message(&g_fake_transport, &reply_msg, NULL); assert(res == _Z_RES_OK); // Remote reply buffered (LATEST mode), not delivered yet, // will be delivered on RESPONSE_FINAL assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 0); assert(g_session._pending_queries != NULL); // Receiving RESPONSE_FINAL from remote queryable _z_network_message_t final_msg; _z_n_msg_make_response_final(&final_msg, request_id); res = _z_handle_network_message(&g_fake_transport, &final_msg, NULL); assert(res == _Z_RES_OK); // Remote reply delivered, query finalized assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 1); assert(g_session._pending_queries == NULL); _z_unregister_session_queryable(&g_session, &queryable_primary); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_query_local_and_remote_via_api(void) { setup_session(); add_fake_peer(); const char *kestr = "zenoh-pico/tests/local/query/api-mixed"; _z_declared_keyexpr_t keyexpr = create_local_resource(kestr); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_final_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); z_owned_closure_query_t q_closure = {0}; assert(z_closure_query(&q_closure, (z_closure_query_callback_t)local_query_callback, NULL, &g_local_query_delivery_count) == Z_OK); z_owned_queryable_t queryable = {0}; z_queryable_options_t qopt; z_queryable_options_default(&qopt); qopt.allowed_origin = Z_LOCALITY_ANY; z_owned_keyexpr_t keyexpr_owned = {0}; assert(z_keyexpr_from_str(&keyexpr_owned, kestr) == Z_OK); z_result_t res = z_declare_queryable((const z_loaned_session_t *)&g_session_rc, &queryable, (const z_loaned_keyexpr_t *)&keyexpr_owned, z_move(q_closure), &qopt); assert(res == Z_OK); z_owned_closure_reply_t r_closure = {0}; assert(z_closure_reply(&r_closure, query_reply_callback, query_dropper, NULL) == Z_OK); z_get_options_t gopt; z_get_options_default(&gopt); gopt.allowed_destination = Z_LOCALITY_ANY; res = z_get((const z_loaned_session_t *)&g_session_rc, (const z_loaned_keyexpr_t *)&keyexpr_owned, "", z_move(r_closure), &gopt); assert(res == Z_OK); _z_pending_query_t *pq = _z_pending_query_slist_value(g_session._pending_queries); assert(pq != NULL); _z_zint_t request_id = pq->_id; const char remote_data[] = "remote-response"; _z_bytes_t remote_payload = _z_bytes_null(); assert(_z_bytes_from_buf(&remote_payload, (const uint8_t *)remote_data, sizeof(remote_data) - 1) == _Z_RES_OK); _z_id_t remote_zid = _z_id_empty(); _z_session_generate_zid(&remote_zid, Z_ZID_LENGTH); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t timestamp = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); _z_network_message_t reply_msg; _z_wireexpr_t wireexpr = _z_declared_keyexpr_alias_to_wire(&keyexpr, &g_session); _z_n_msg_make_reply_ok_put(&reply_msg, &remote_zid, request_id, &wireexpr, Z_RELIABILITY_RELIABLE, Z_CONSOLIDATION_MODE_DEFAULT, _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT), ×tamp, &source_info, &remote_payload, &encoding, NULL); res = _z_handle_network_message(&g_fake_transport, &reply_msg, NULL); assert(res == _Z_RES_OK); _z_network_message_t final_msg; _z_n_msg_make_response_final(&final_msg, request_id); res = _z_handle_network_message(&g_fake_transport, &final_msg, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_query_reply_callback_count, memory_order_relaxed) == 1); assert(atomic_load_explicit(&g_query_drop_callback_count, memory_order_relaxed) == 1); assert(g_session._pending_queries == NULL); z_moved_queryable_t *mq = z_queryable_move(&queryable); z_queryable_drop(mq); z_moved_keyexpr_t *mk = z_keyexpr_move(&keyexpr_owned); z_keyexpr_drop(mk); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_put_remote_only_destination(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/remote-only"); _z_subscription_rc_t sub = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_ANY); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; _z_bytes_t payload; assert(_z_bytes_from_buf(&payload, (const uint8_t *)payload_data, sizeof(payload_data) - 1) == _Z_RES_OK); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t ts = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); z_result_t res = _z_write(&g_session, &keyexpr, &payload, &encoding, Z_SAMPLE_KIND_PUT, Z_CONGESTION_CONTROL_BLOCK, Z_PRIORITY_DEFAULT, false, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info, Z_LOCALITY_REMOTE); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); _z_bytes_drop(&payload); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_subscriber_remote_only_origin(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/put/remote-origin"); _z_subscription_rc_t sub = register_local_subscription(&keyexpr, &g_local_put_delivery_count, Z_LOCALITY_REMOTE); atomic_store_explicit(&g_local_put_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); const char payload_data[] = "payload"; _z_bytes_t payload; assert(_z_bytes_from_buf(&payload, (const uint8_t *)payload_data, sizeof(payload_data) - 1) == _Z_RES_OK); _z_encoding_t encoding = _z_encoding_null(); _z_timestamp_t ts = _z_timestamp_null(); _z_source_info_t source_info = _z_source_info_null(); z_result_t res = _z_write(&g_session, &keyexpr, &payload, &encoding, Z_SAMPLE_KIND_PUT, Z_CONGESTION_CONTROL_BLOCK, Z_PRIORITY_DEFAULT, false, &ts, NULL, Z_RELIABILITY_RELIABLE, &source_info, Z_LOCALITY_ANY); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_put_delivery_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); _z_bytes_drop(&payload); _z_unregister_subscription(&g_session, _Z_SUBSCRIBER_KIND_SUBSCRIBER, &sub); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_query_remote_only_destination(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/query/remote-only"); _z_session_queryable_rc_t queryable_rc = register_local_queryable(&keyexpr, &g_local_query_delivery_count, Z_LOCALITY_ANY); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_reply_callback_count, 0, memory_order_relaxed); atomic_store_explicit(&g_query_drop_callback_count, 0, memory_order_relaxed); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); z_result_t res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_REMOTE, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); // Clean pending query by simulating RESPONSE_FINAL _z_pending_query_t *pq = _z_pending_query_slist_value(g_session._pending_queries); assert(pq != NULL); _z_network_message_t final_msg; _z_n_msg_make_response_final(&final_msg, pq->_id); res = _z_handle_network_message(&g_fake_transport, &final_msg, NULL); assert(res == _Z_RES_OK); assert(g_session._pending_queries == NULL); _z_unregister_session_queryable(&g_session, &queryable_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } static void test_queryable_remote_only_origin(void) { setup_session(); add_fake_peer(); _z_declared_keyexpr_t keyexpr = create_local_resource("zenoh-pico/tests/local/query/remote-origin"); _z_session_queryable_rc_t queryable_rc = register_local_queryable(&keyexpr, &g_local_query_delivery_count, Z_LOCALITY_REMOTE); atomic_store_explicit(&g_local_query_delivery_count, 0, memory_order_relaxed); atomic_store_explicit(&g_network_send_count, 0, memory_order_relaxed); _z_n_qos_t qos = _z_n_qos_make(false, false, Z_PRIORITY_DEFAULT); z_result_t res = _z_query(&g_session_rc, _z_optional_id_make_none(), &keyexpr, NULL, 0, Z_QUERY_TARGET_DEFAULT, Z_CONSOLIDATION_MODE_LATEST, NULL, NULL, query_reply_callback, query_dropper, NULL, 1000, NULL, qos, NULL, Z_REPLY_KEYEXPR_MATCHING_QUERY, Z_LOCALITY_ANY, NULL); assert(res == _Z_RES_OK); assert(atomic_load_explicit(&g_local_query_delivery_count, memory_order_relaxed) == 0); assert(atomic_load_explicit(&g_network_send_count, memory_order_relaxed) == 1); _z_pending_query_t *pq = _z_pending_query_slist_value(g_session._pending_queries); assert(pq != NULL); _z_network_message_t final_msg2; _z_n_msg_make_response_final(&final_msg2, pq->_id); res = _z_handle_network_message(&g_fake_transport, &final_msg2, NULL); assert(res == _Z_RES_OK); _z_unregister_session_queryable(&g_session, &queryable_rc); cleanup_local_resource(&keyexpr); cleanup_session(); } int main(void) { test_put_local_only_single(); test_put_local_only_via_api(); test_put_local_and_remote_via_api(); test_put_local_only_multiple(); test_put_local_and_remote(); test_query_local_only_single(); test_query_local_only_multiple(); test_query_local_and_remote(); test_query_local_and_remote_via_api(); test_put_remote_only_destination(); test_subscriber_remote_only_origin(); test_query_remote_only_destination(); test_queryable_remote_only_origin(); return 0; } #else int main(void) { printf( "This test requires: Z_FEATURE_SUBSCRIPTION, Z_FEATURE_QUERYABLE, Z_FEATURE_QUERY, Z_FEATURE_LOCAL_SUBSCRIBER" "and Z_FEATURE_LOCAL_QUERYABLE.\n"); return 0; } #endif ================================================ FILE: tests/z_lru_cache_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/collections/lru_cache.h" #include "zenoh-pico/system/common/platform.h" #undef NDEBUG #include #define CACHE_CAPACITY 10 typedef struct _dummy_t { int foo; } _dummy_t; int _dummy_compare(const void *first, const void *second) { _dummy_t *d_first = (_dummy_t *)first; _dummy_t *d_second = (_dummy_t *)second; if (d_first->foo == d_second->foo) { return 0; } else if (d_first->foo > d_second->foo) { return 1; } return -1; } static inline void _dummy_elem_clear(void *e) { _z_noop_clear((_dummy_t *)e); } _Z_LRU_CACHE_DEFINE(_dummy, _dummy_t, _dummy_compare) void test_lru_init(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(CACHE_CAPACITY); assert(dcache.capacity == CACHE_CAPACITY); assert(dcache.len == 0); assert(dcache.head == NULL); assert(dcache.tail == NULL); assert(dcache.slist == NULL); } void test_lru_cache_insert(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(CACHE_CAPACITY); _dummy_t v0 = {0}; assert(dcache.slist == NULL); assert(_dummy_lru_cache_get(&dcache, &v0) == NULL); assert(_dummy_lru_cache_insert(&dcache, &v0) == 0); assert(dcache.slist != NULL); _dummy_t *res = _dummy_lru_cache_get(&dcache, &v0); assert(res != NULL); assert(res->foo == v0.foo); _dummy_t data[CACHE_CAPACITY] = {0}; for (size_t i = 1; i < CACHE_CAPACITY; i++) { data[i].foo = (int)i; assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } for (size_t i = 0; i < CACHE_CAPACITY; i++) { res = _dummy_lru_cache_get(&dcache, &data[i]); assert(res != NULL); assert(res->foo == data[i].foo); } _dummy_lru_cache_delete(&dcache); } void test_lru_cache_clear(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(CACHE_CAPACITY); _dummy_t data[CACHE_CAPACITY] = {0}; for (size_t i = 0; i < CACHE_CAPACITY; i++) { data[i].foo = (int)i; assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } _dummy_lru_cache_clear(&dcache); assert(dcache.capacity == CACHE_CAPACITY); assert(dcache.len == 0); assert(dcache.slist != NULL); assert(dcache.head == NULL); assert(dcache.tail == NULL); for (size_t i = 0; i < CACHE_CAPACITY; i++) { assert(_dummy_lru_cache_get(&dcache, &data[i]) == NULL); } _dummy_lru_cache_delete(&dcache); } void test_lru_cache_deletion(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(CACHE_CAPACITY); _dummy_t data[CACHE_CAPACITY + 1] = {0}; for (size_t i = 0; i < CACHE_CAPACITY + 1; i++) { data[i].foo = (int)i; assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } // Check value deleted assert(_dummy_lru_cache_get(&dcache, &data[0]) == NULL); // Check remaining value for (size_t i = 1; i < CACHE_CAPACITY + 1; i++) { _dummy_t *res = _dummy_lru_cache_get(&dcache, &data[i]); assert(res != NULL); assert(res->foo == data[i].foo); } _dummy_lru_cache_delete(&dcache); } void test_lru_cache_update(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(CACHE_CAPACITY); _dummy_t data[CACHE_CAPACITY] = {0}; for (size_t i = 0; i < CACHE_CAPACITY; i++) { data[i].foo = (int)i; assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } // Update value assert(_dummy_lru_cache_get(&dcache, &data[0]) != NULL); // Insert extra value _dummy_t extra_data = {55}; assert(_dummy_lru_cache_insert(&dcache, &extra_data) == 0); // Check value deleted assert(_dummy_lru_cache_get(&dcache, &data[1]) == NULL); _dummy_lru_cache_delete(&dcache); } static bool val_in_array(int val, int *array, size_t array_size) { for (size_t i = 0; i < array_size; i++) { if (val == array[i]) { return true; } } return false; } void test_lru_cache_random_val(void) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(10 * CACHE_CAPACITY); _dummy_t data[11 * CACHE_CAPACITY] = { {1804289383}, {846930886}, {1681692777}, {1714636915}, {1957747793}, {424238335}, {719885386}, {1649760492}, {596516649}, {1189641421}, {1025202362}, {1350490027}, {783368690}, {1102520059}, {2044897763}, {1967513926}, {1365180540}, {1540383426}, {304089172}, {1303455736}, {35005211}, {521595368}, {294702567}, {1726956429}, {336465782}, {861021530}, {278722862}, {233665123}, {2145174067}, {468703135}, {1101513929}, {1801979802}, {1315634022}, {635723058}, {1369133069}, {1125898167}, {1059961393}, {2089018456}, {628175011}, {1656478042}, {1131176229}, {1653377373}, {859484421}, {1914544919}, {608413784}, {756898537}, {1734575198}, {1973594324}, {149798315}, {2038664370}, {1129566413}, {184803526}, {412776091}, {1424268980}, {1911759956}, {749241873}, {137806862}, {42999170}, {982906996}, {135497281}, {511702305}, {2084420925}, {1937477084}, {1827336327}, {572660336}, {1159126505}, {805750846}, {1632621729}, {1100661313}, {1433925857}, {1141616124}, {84353895}, {939819582}, {2001100545}, {1998898814}, {1548233367}, {610515434}, {1585990364}, {1374344043}, {760313750}, {1477171087}, {356426808}, {945117276}, {1889947178}, {1780695788}, {709393584}, {491705403}, {1918502651}, {752392754}, {1474612399}, {2053999932}, {1264095060}, {1411549676}, {1843993368}, {943947739}, {1984210012}, {855636226}, {1749698586}, {1469348094}, {1956297539}, {1036140795}, {463480570}, {2040651434}, {1975960378}, {317097467}, {1892066601}, {1376710097}, {927612902}, {1330573317}, {603570492}, }; // Insert data for (size_t i = 0; i < _ZP_ARRAY_SIZE(data); i++) { assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } // Check values for (size_t i = 0; i < _ZP_ARRAY_SIZE(data); i++) { _dummy_t *res = _dummy_lru_cache_get(&dcache, &data[i]); if (i < CACHE_CAPACITY) { assert(res == NULL); } else { assert(res != NULL); assert(res->foo == data[i].foo); } } // Update most values int not_upd_idx[CACHE_CAPACITY] = { 34, 12, 42, 46, 56, 109, 103, 15, 96, 31, }; for (size_t i = CACHE_CAPACITY; i < _ZP_ARRAY_SIZE(data); i++) { if (!val_in_array((int)i, not_upd_idx, _ZP_ARRAY_SIZE(not_upd_idx))) { assert(_dummy_lru_cache_get(&dcache, &data[i]) != NULL); } } // Insert back deleted value for (size_t i = 0; i < CACHE_CAPACITY; i++) { assert(_dummy_lru_cache_get(&dcache, &data[i]) == NULL); assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } // Check deleted values for (size_t i = 0; i < _ZP_ARRAY_SIZE(data); i++) { _dummy_t *res = _dummy_lru_cache_get(&dcache, &data[i]); if (val_in_array((int)i, not_upd_idx, _ZP_ARRAY_SIZE(not_upd_idx))) { assert(res == NULL); } else { assert(res != NULL); assert(res->foo == data[i].foo); } } // Clean-up _dummy_lru_cache_delete(&dcache); } #if 0 #define BENCH_THRESHOLD 1000000 void test_search_benchmark(size_t capacity) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(capacity); _dummy_t data[capacity]; memset(data, 0, capacity * sizeof(_dummy_t)); srand(0x55); // Insert data for (size_t i = 0; i < _ZP_ARRAY_SIZE(data); i++) { data[i].foo = rand(); assert(_dummy_lru_cache_insert(&dcache, &data[i]) == 0); } z_clock_t measure_start = z_clock_now(); for (size_t get_cnt = 0; get_cnt <= BENCH_THRESHOLD; get_cnt++) { int i = rand() % (int)_ZP_ARRAY_SIZE(data); _dummy_t *src = &data[i]; _dummy_t *res = _dummy_lru_cache_get(&dcache, src); assert(res != NULL); } unsigned long elapsed_us = z_clock_elapsed_us(&measure_start); printf("Time to search: %ld\n", elapsed_us); } void test_insert_benchmark(size_t capacity) { _dummy_lru_cache_t dcache = _dummy_lru_cache_init(capacity); srand(0x55); // Insert data z_clock_t measure_start = z_clock_now(); for (size_t get_cnt = 0; get_cnt <= BENCH_THRESHOLD; get_cnt++) { _dummy_t data = {.foo = rand()}; assert(_dummy_lru_cache_insert(&dcache, &data) == 0); } unsigned long elapsed_us = z_clock_elapsed_us(&measure_start); printf("Time to insert: %ld\n", elapsed_us); } void test_benchmark(void) { for (size_t i = 1; i <= BENCH_THRESHOLD; i *= 10) { printf("Capacity: %ld\n", i); test_search_benchmark(i); test_insert_benchmark(i); } } #endif int main(void) { test_lru_init(); test_lru_cache_insert(); test_lru_cache_clear(); test_lru_cache_deletion(); test_lru_cache_update(); test_lru_cache_random_val(); #if 0 test_benchmark(); #endif return 0; } ================================================ FILE: tests/z_msgcodec_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include "zenoh-pico/api/constants.h" #include "zenoh-pico/protocol/codec/message.h" #include "zenoh-pico/protocol/codec/network.h" #include "zenoh-pico/protocol/codec/serial.h" #include "zenoh-pico/protocol/definitions/message.h" #include "zenoh-pico/protocol/definitions/transport.h" #include "zenoh-pico/utils/query_params.h" #define ZENOH_PICO_TEST_H #include #include #include #include #include #include #include "zenoh-pico/collections/slice.h" #include "zenoh-pico/collections/string.h" #include "zenoh-pico/protocol/codec/core.h" #include "zenoh-pico/protocol/codec/declarations.h" #include "zenoh-pico/protocol/codec/ext.h" #include "zenoh-pico/protocol/codec/interest.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/protocol/core.h" #include "zenoh-pico/protocol/definitions/declarations.h" #include "zenoh-pico/protocol/definitions/interest.h" #include "zenoh-pico/protocol/definitions/network.h" #include "zenoh-pico/protocol/ext.h" #include "zenoh-pico/protocol/iobuf.h" #include "zenoh-pico/system/platform.h" #undef NDEBUG #include #define RUNS 1000 #define _Z_MOCK_EXTENSION_UNIT 0x01 #define _Z_MOCK_EXTENSION_ZINT 0x02 #define _Z_MOCK_EXTENSION_ZBUF 0x03 #if defined(_WIN32) || defined(WIN32) #pragma warning(push) #pragma warning(disable : 4244 4267) // Disable truncation warnings in MSVC, // as it is used voluntarily in this file when working with RNG #endif /*=============================*/ /* Helper functions */ /*=============================*/ void print_iosli(_z_iosli_t *ios) { printf("IOSli: Capacity: %zu, Rpos: %zu, Wpos: %zu, Buffer: [", ios->_capacity, ios->_r_pos, ios->_w_pos); for (size_t i = ios->_r_pos; i < ios->_w_pos; i++) { printf("%02x", ios->_buf[i]); if (i < ios->_w_pos - ios->_r_pos - 1) printf(" "); } printf("]"); } void print_wbuf(_z_wbuf_t *wbf) { printf(" WBuf: %zu, RIdx: %zu, WIdx: %zu\n", _z_wbuf_len_iosli(wbf), wbf->_r_idx, wbf->_w_idx); for (size_t i = 0; i < _z_wbuf_len_iosli(wbf); i++) { printf(" - "); print_iosli(_z_wbuf_get_iosli(wbf, i)); printf("\n"); } } void print_zbuf(_z_zbuf_t *zbf) { print_iosli(&zbf->_ios); } void print_uint8_array(_z_slice_t *arr) { printf("Length: %zu, Buffer: [", arr->len); for (size_t i = 0; i < arr->len; i++) { printf("%02x", arr->start[i]); if (i < arr->len - 1) printf(" "); } printf("]"); } void print_transport_message_type(uint8_t header) { switch (_Z_MID(header)) { case _Z_MID_T_JOIN: printf("Join message"); break; case _Z_MID_T_INIT: printf("Init message"); break; case _Z_MID_T_OPEN: printf("Open message"); break; case _Z_MID_T_CLOSE: printf("Close message"); break; case _Z_MID_T_KEEP_ALIVE: printf("KeepAlive message"); break; case _Z_MID_T_FRAME: printf("Frame message"); break; case _Z_MID_T_FRAGMENT: printf("Frame message"); break; default: assert(0); break; } } void print_scouting_message_type(uint8_t header) { switch (_Z_MID(header)) { case _Z_MID_SCOUT: printf("Scout message"); break; case _Z_MID_HELLO: printf("Hello message"); break; default: assert(0); break; } } /*=============================*/ /* Generating functions */ /*=============================*/ bool gen_bool(void) { return z_random_u8() % 2; } uint8_t gen_uint8(void) { return z_random_u8(); } uint16_t gen_uint16(void) { return z_random_u16(); } uint64_t gen_uint64(void) { uint64_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } uint32_t gen_uint32(void) { unsigned int ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } _z_zint_t gen_zint(void) { _z_zint_t ret = 0; z_random_fill(&ret, sizeof(ret)); return ret; } _z_wbuf_t gen_wbuf(size_t len) { bool is_expandable = false; if (gen_bool()) { is_expandable = true; len = 1 + (gen_zint() % len); } _z_wbuf_t wbuf = _z_wbuf_make(len, is_expandable); assert(_z_wbuf_capacity(&wbuf) == len); return wbuf; } _z_slice_t gen_slice(size_t len) { if (len == 0) { return _z_slice_null(); } uint8_t *p = (uint8_t *)z_malloc(sizeof(uint8_t) * len); for (_z_zint_t i = 0; i < len; i++) { ((uint8_t *)p)[i] = gen_uint8() & 0x7f; // 0b01111111 } return _z_slice_from_buf_custom_deleter(p, len, _z_delete_context_default()); } _z_bytes_t gen_payload(size_t len) { _z_slice_t pld = gen_slice(len); _z_bytes_t b; _z_bytes_from_slice(&b, &pld); return b; } _z_bytes_t gen_bytes(size_t len) { _z_slice_t s = gen_slice(len); _z_bytes_t b; _z_bytes_from_slice(&b, &s); return b; } _z_id_t gen_zid(void) { _z_id_t id = _z_id_empty(); uint8_t hash = 55; uint8_t len = gen_uint8() % 16; for (uint8_t i = 0; i < len; i++) { uint8_t byte = gen_uint8(); id.id[i] = byte; hash = (uint8_t)(hash ^ i * byte); } id.id[0] = hash; return id; } char *gen_str(size_t size) { char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; char *str = (char *)z_malloc(size + 1); for (_z_zint_t i = 0; i < size; i++) { uint32_t key = z_random_u32() % (sizeof(charset) - 1); str[i] = charset[key]; } str[size] = '\0'; return str; } _z_string_t gen_string(size_t len) { char *str = gen_str(len); _z_string_t ret = _z_string_copy_from_str(str); z_free(str); return ret; } _z_string_svec_t gen_str_array(size_t size) { _z_string_svec_t sa = _z_string_svec_make(size); for (size_t i = 0; i < size; i++) { _z_string_t s = gen_string(16); assert(_z_string_svec_append(&sa, &s, true) == _Z_RES_OK); } return sa; } _z_locator_array_t gen_locator_array(size_t size) { _z_locator_array_t la = _z_locator_array_make(size); for (size_t i = 0; i < size; i++) { _z_locator_t *val = &la._val[i]; val->_protocol = gen_string(3); val->_address = gen_string(12); val->_metadata = _z_str_intmap_make(); // @TODO: generate metadata } return la; } _z_encoding_t gen_encoding(void) { _z_encoding_t en; en.id = gen_uint16(); if (gen_bool()) { en.schema = gen_string(16); } else { en.schema = _z_string_null(); } return en; } _z_value_t gen_value(void) { _z_value_t val; val.encoding = gen_encoding(); if (gen_bool()) { val.payload = _z_bytes_null(); } else { val.payload = gen_bytes(16); } return val; } /*=============================*/ /* Asserting functions */ /*=============================*/ void assert_eq_iosli(_z_iosli_t *left, _z_iosli_t *right) { printf("IOSli -> "); printf("Capacity (%zu:%zu), ", left->_capacity, right->_capacity); assert(left->_capacity == right->_capacity); printf("Content ("); for (_z_zint_t i = 0; i < left->_capacity; i++) { uint8_t l = left->_buf[i]; uint8_t r = right->_buf[i]; printf("%02x:%02x", l, r); if (i < left->_capacity - 1) printf(" "); assert(l == r); } printf(")"); } void assert_eq_uint8_array(const _z_slice_t *left, const _z_slice_t *right) { printf("Array -> "); printf("Length (%zu:%zu), ", left->len, right->len); assert(left->len == right->len); printf("Content ("); for (size_t i = 0; i < left->len; i++) { uint8_t l = left->start[i]; uint8_t r = right->start[i]; printf("%02x:%02x", l, r); if (i < left->len - 1) printf(" "); assert(l == r); } printf(")"); } void assert_eq_string_array(_z_string_svec_t *left, _z_string_svec_t *right) { printf("Array -> "); printf("Length (%zu:%zu), ", left->_len, right->_len); assert(left->_len == right->_len); printf("Content ("); for (size_t i = 0; i < left->_len; i++) { const char *l = _z_string_data(_z_string_svec_get(left, i)); const char *r = _z_string_data(_z_string_svec_get(right, i)); size_t l_len = _z_string_len(_z_string_svec_get(left, i)); size_t r_len = _z_string_len(_z_string_svec_get(right, i)); printf("%.*s:%.*s", (int)l_len, l, (int)r_len, r); if (i < left->_len - 1) printf(" "); assert(_z_string_equals(_z_string_svec_get(left, i), _z_string_svec_get(right, i))); } printf(")"); } void assert_eq_locator_array(const _z_locator_array_t *left, const _z_locator_array_t *right) { printf("Locators -> "); printf("Length (%zu:%zu), ", left->_len, right->_len); assert(left->_len == right->_len); printf("Content ("); for (size_t i = 0; i < left->_len; i++) { const _z_locator_t *l = &left->_val[i]; const _z_locator_t *r = &right->_val[i]; _z_string_t ls = _z_locator_to_string(l); _z_string_t rs = _z_locator_to_string(r); printf("%.*s:%.*s", (int)_z_string_len(&ls), _z_string_data(&ls), (int)_z_string_len(&rs), _z_string_data(&rs)); if (i < left->_len - 1) printf(" "); _z_string_clear(&ls); _z_string_clear(&rs); assert(_z_locator_eq(l, r) == true); } printf(")"); } /*=============================*/ /* Zenoh Core Fields */ /*=============================*/ void zint(void) { printf("\n>> ZINT\n"); _z_wbuf_t wbf = gen_wbuf(9); // Initialize _z_zint_t e_z = gen_zint(); // Encode z_result_t res = _z_zsize_encode(&wbf, e_z); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_zint_t d_z; res = _z_zsize_decode(&d_z, &zbf); assert(res == _Z_RES_OK); assert(e_z == d_z); // Free _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*=============================*/ /* Zenoh Messages Extensions */ /*=============================*/ /*------------------ UNIT extension ------------------*/ _z_msg_ext_t gen_unit_extension(void) { return _z_msg_ext_make_unit(_Z_MOCK_EXTENSION_UNIT); } void assert_eq_unit_extension(_z_msg_ext_unit_t *left, _z_msg_ext_unit_t *right) { (void)(left); (void)(right); } void unit_extension(void) { printf("\n>> UNIT Extension\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_msg_ext_t ext = gen_unit_extension(); assert(_Z_EXT_ENC(ext._header) == _Z_MSG_EXT_ENC_UNIT); _z_msg_ext_unit_t e_u = ext._body._unit; // Encode z_result_t res = _z_msg_ext_encode_unit(&wbf, &e_u); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_msg_ext_unit_t d_u; res = _z_msg_ext_decode_unit(&d_u, &zbf); assert(res == _Z_RES_OK); assert_eq_unit_extension(&e_u, &d_u); // Free _z_msg_ext_clear_unit(&d_u); _z_msg_ext_clear(&ext); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ ZINT extension ------------------*/ _z_msg_ext_t gen_zint_extension(void) { _z_zint_t val = gen_zint(); return _z_msg_ext_make_zint(_Z_MOCK_EXTENSION_ZINT, val); } void assert_eq_zint_extension(_z_msg_ext_zint_t *left, _z_msg_ext_zint_t *right) { printf(" ZINT (%ju:%ju), ", (uintmax_t)left->_val, (uintmax_t)right->_val); assert(left->_val == right->_val); } void zint_extension(void) { printf("\n>> ZINT Extension\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_msg_ext_t ext = gen_zint_extension(); assert(_Z_EXT_ENC(ext._header) == _Z_MSG_EXT_ENC_ZINT); _z_msg_ext_zint_t e_u = ext._body._zint; // Encode z_result_t res = _z_msg_ext_encode_zint(&wbf, &e_u); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_msg_ext_zint_t d_u; res = _z_msg_ext_decode_zint(&d_u, &zbf); assert(res == _Z_RES_OK); assert_eq_zint_extension(&e_u, &d_u); // Free _z_msg_ext_clear_zint(&d_u); _z_msg_ext_clear(&ext); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Unit extension ------------------*/ _z_msg_ext_t gen_zbuf_extension(void) { _z_slice_t val = gen_slice(gen_uint8()); return _z_msg_ext_make_zbuf(_Z_MOCK_EXTENSION_ZBUF, val); } void assert_eq_zbuf_extension(_z_msg_ext_zbuf_t *left, _z_msg_ext_zbuf_t *right) { printf(" ZBUF ("); assert_eq_uint8_array(&left->_val, &right->_val); printf(")"); } void zbuf_extension(void) { printf("\n>> ZBUF Extension\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_msg_ext_t ext = gen_zbuf_extension(); assert(_Z_EXT_ENC(ext._header) == _Z_MSG_EXT_ENC_ZBUF); _z_msg_ext_zbuf_t e_u = ext._body._zbuf; // Encode z_result_t res = _z_msg_ext_encode_zbuf(&wbf, &e_u); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_msg_ext_zbuf_t d_u; res = _z_msg_ext_decode_zbuf(&d_u, &zbf); assert(res == _Z_RES_OK); assert_eq_zbuf_extension(&e_u, &d_u); // Free _z_msg_ext_clear_zbuf(&d_u); _z_msg_ext_clear(&ext); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*=============================*/ /* Message Fields */ /*=============================*/ /*------------------ Payload field ------------------*/ void assert_eq_slice(const _z_slice_t *left, const _z_slice_t *right) { assert_eq_uint8_array(left, right); } void assert_eq_string(const _z_string_t *left, const _z_string_t *right) { assert(_z_string_len(left) == _z_string_len(right)); if (_z_string_len(left) > 0) { assert(_z_string_equals(left, right)); } } void assert_eq_bytes(const _z_bytes_t *left, const _z_bytes_t *right) { size_t len_left = _z_bytes_len(left); size_t len_right = _z_bytes_len(right); printf("Array -> "); printf("Length (%zu:%zu), ", len_left, len_right); assert(len_left == len_right); printf("Content ("); _z_bytes_reader_t reader_left = _z_bytes_get_reader(left); _z_bytes_reader_t reader_right = _z_bytes_get_reader(right); for (size_t i = 0; i < len_left; i++) { uint8_t l = 0, r = 0; _z_bytes_reader_read(&reader_left, &l, 1); _z_bytes_reader_read(&reader_right, &r, 1); printf("%02x:%02x", l, r); if (i < len_left - 1) printf(" "); assert(l == r); } printf(")"); } void payload_field(void) { printf("\n>> Payload field\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_bytes_t e_pld = gen_payload(64); // Encode z_result_t res = _z_bytes_encode(&wbf, &e_pld); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_bytes_t d_pld = _z_bytes_null(); _z_arc_slice_t arcs = {0}; res = _z_bytes_decode(&d_pld, &zbf, &arcs); assert(res == _Z_RES_OK); printf(" "); assert_eq_bytes(&e_pld, &d_pld); printf("\n"); // Free _z_bytes_drop(&e_pld); _z_bytes_drop(&d_pld); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void assert_eq_encoding(const _z_encoding_t *left, const _z_encoding_t *right) { assert(left->id == right->id); assert_eq_string(&left->schema, &right->schema); } void assert_eq_value(const _z_value_t *left, const _z_value_t *right) { assert_eq_encoding(&left->encoding, &right->encoding); assert_eq_bytes(&left->payload, &right->payload); } /*----------------- Source info field -----------------*/ _z_source_info_t gen_source_info(void) { return (_z_source_info_t){ ._source_id.zid = gen_zid(), ._source_sn = (uint32_t)gen_uint64(), ._source_id.eid = (uint32_t)gen_uint64()}; } void assert_eq_source_info(const _z_source_info_t *left, const _z_source_info_t *right) { assert(left->_source_sn == right->_source_sn); assert(left->_source_id.eid == right->_source_id.eid); assert(memcmp(left->_source_id.zid.id, right->_source_id.zid.id, 16) == 0); } void source_info_field(void) { printf("\n>> Source info field\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_source_info_t e_si = gen_source_info(); printf("eid: %u, sn: %u\n", e_si._source_id.eid, e_si._source_sn); // Encode z_result_t res = _z_source_info_encode(&wbf, &e_si); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); print_wbuf(&wbf); print_iosli(&zbf._ios); printf("\n"); _z_source_info_t d_si; res = _z_source_info_decode(&d_si, &zbf); assert(res == _Z_RES_OK); printf("eid: %u, sn: %u\n", d_si._source_id.eid, d_si._source_sn); printf(" "); assert_eq_source_info(&e_si, &d_si); printf("\n"); // Free _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Timestamp field ------------------*/ _z_timestamp_t gen_timestamp(void) { _z_timestamp_t ts; ts.valid = true; ts.time = gen_uint64(); for (size_t i = 0; i < 16; i++) { ts.id.id[i] = gen_uint8() & 0x7f; // 0b01111111 } return ts; } void assert_eq_timestamp(const _z_timestamp_t *left, const _z_timestamp_t *right) { printf("Timestamp -> "); printf("Time (%llu:%llu), ", (unsigned long long)left->time, (unsigned long long)right->time); assert(left->time == right->time); printf("ID ("); for (int i = 0; i < 16; i++) { printf("%02x:%02x, ", left->id.id[i], right->id.id[i]); } printf(")\n"); assert(memcmp(left->id.id, right->id.id, 16) == 0); } void timestamp_field(void) { printf("\n>> Timestamp field\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_timestamp_t e_ts = gen_timestamp(); // Encode z_result_t res = _z_timestamp_encode(&wbf, &e_ts); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); print_wbuf(&wbf); print_iosli(&zbf._ios); printf("\n"); _z_timestamp_t d_ts; res = _z_timestamp_decode(&d_ts, &zbf); assert(res == _Z_RES_OK); printf(" "); assert_eq_timestamp(&e_ts, &d_ts); printf("\n"); // Free _z_timestamp_clear(&e_ts); _z_timestamp_clear(&d_ts); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ ResKey field ------------------*/ _z_wireexpr_t gen_wireexpr(void) { _z_wireexpr_t key; key._id = gen_uint16(); key._mapping = gen_uint32(); bool is_numerical = gen_bool(); if (is_numerical == true) { key._suffix = _z_string_null(); } else { size_t len = gen_zint() % 16 + 1; key._suffix = _z_string_preallocate(len); char *suffix = gen_str(len); memcpy((char *)_z_string_data(&key._suffix), suffix, len); z_free(suffix); } return key; } void assert_eq_keyexpr(const _z_wireexpr_t *left, const _z_wireexpr_t *right) { printf("ResKey -> "); printf("ID (%u:%u), ", left->_id, right->_id); assert(left->_id == right->_id); assert(_z_wireexpr_has_suffix(left) == _z_wireexpr_has_suffix(right)); printf("Name ("); if (_z_wireexpr_has_suffix(left)) { printf("%.*s:%.*s", (int)_z_string_len(&left->_suffix), _z_string_data(&left->_suffix), (int)_z_string_len(&right->_suffix), _z_string_data(&right->_suffix)); assert(_z_string_equals(&left->_suffix, &right->_suffix) == true); } else { printf("NULL:NULL"); } printf(")"); } void keyexpr_field(void) { printf("\n>> ResKey field\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_wireexpr_t e_rk = gen_wireexpr(); // Encode uint8_t header = (_z_wireexpr_has_suffix(&e_rk)) ? _Z_FLAG_Z_K : 0; z_result_t res = _z_wireexpr_encode(&wbf, _Z_HAS_FLAG(header, _Z_FLAG_Z_K), &e_rk); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_wireexpr_t d_rk; res = _z_wireexpr_decode(&d_rk, &zbf, _Z_HAS_FLAG(header, _Z_FLAG_Z_K), false, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_keyexpr(&e_rk, &d_rk); printf("\n"); // Free _z_wireexpr_clear(&e_rk); _z_wireexpr_clear(&d_rk); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*=============================*/ /* Declaration Fields */ /*=============================*/ /*------------------ Resource declaration ------------------*/ _z_decl_kexpr_t gen_resource_declaration(void) { return (_z_decl_kexpr_t){._id = gen_uint16(), ._keyexpr = gen_wireexpr()}; } void assert_eq_resource_declaration(const _z_decl_kexpr_t *left, const _z_decl_kexpr_t *right) { printf("RID (%u:%u), ", left->_id, right->_id); assert(left->_id == right->_id); assert_eq_keyexpr(&left->_keyexpr, &right->_keyexpr); } void resource_declaration(void) { printf("\n>> Resource declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_decl_kexpr_t e_rd = gen_resource_declaration(); // Encode z_result_t res = _z_decl_kexpr_encode(&wbf, &e_rd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_decl_kexpr_t d_rd; uint8_t e_hdr; _z_uint8_decode(&e_hdr, &zbf); res = _z_decl_kexpr_decode(&d_rd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_resource_declaration(&e_rd, &d_rd); printf("\n"); // Free _z_wireexpr_clear(&e_rd._keyexpr); _z_wireexpr_clear(&d_rd._keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Subscriber declaration ------------------*/ _z_decl_subscriber_t gen_subscriber_declaration(void) { _z_decl_subscriber_t e_sd = {._keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_uint64()}; return e_sd; } void assert_eq_subscriber_declaration(const _z_decl_subscriber_t *left, const _z_decl_subscriber_t *right) { assert_eq_keyexpr(&left->_keyexpr, &right->_keyexpr); assert(left->_id == right->_id); } void subscriber_declaration(void) { printf("\n>> Subscriber declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_decl_subscriber_t e_sd = gen_subscriber_declaration(); // Encode z_result_t res = _z_decl_subscriber_encode(&wbf, &e_sd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_decl_subscriber_t d_sd; uint8_t e_hdr; _z_uint8_decode(&e_hdr, &zbf); res = _z_decl_subscriber_decode(&d_sd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_subscriber_declaration(&e_sd, &d_sd); printf("\n"); // Free _z_wireexpr_clear(&e_sd._keyexpr); _z_wireexpr_clear(&d_sd._keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Queryable declaration ------------------*/ _z_decl_queryable_t gen_queryable_declaration(void) { _z_decl_queryable_t e_qd = {._keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_uint64(), ._ext_queryable_info = {._complete = gen_uint8(), ._distance = gen_uint16()}}; return e_qd; } void assert_eq_queryable_declaration(const _z_decl_queryable_t *left, const _z_decl_queryable_t *right) { assert_eq_keyexpr(&left->_keyexpr, &right->_keyexpr); printf("Complete (%u:%u), ", left->_ext_queryable_info._complete, right->_ext_queryable_info._complete); assert(left->_ext_queryable_info._complete == right->_ext_queryable_info._complete); printf("Distance (%u:%u), ", left->_ext_queryable_info._distance, right->_ext_queryable_info._distance); assert(left->_ext_queryable_info._distance == right->_ext_queryable_info._distance); } void queryable_declaration(void) { printf("\n>> Queryable declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_decl_queryable_t e_qd = gen_queryable_declaration(); // Encode z_result_t res = _z_decl_queryable_encode(&wbf, &e_qd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_decl_queryable_t d_qd; uint8_t e_hdr = 0; _z_uint8_decode(&e_hdr, &zbf); res = _z_decl_queryable_decode(&d_qd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_queryable_declaration(&e_qd, &d_qd); printf("\n"); // Free _z_wireexpr_clear(&e_qd._keyexpr); _z_wireexpr_clear(&d_qd._keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Token declaration ------------------*/ _z_decl_token_t gen_token_declaration(void) { _z_decl_token_t e_qd = {._keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_uint64()}; return e_qd; } void assert_eq_token_declaration(const _z_decl_token_t *left, const _z_decl_token_t *right) { assert_eq_keyexpr(&left->_keyexpr, &right->_keyexpr); assert(left->_id == right->_id); } void token_declaration(void) { printf("\n>> Queryable declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_decl_token_t e_qd = gen_token_declaration(); // Encode int8_t res = _z_decl_token_encode(&wbf, &e_qd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_decl_token_t d_qd; uint8_t e_hdr = 0; _z_uint8_decode(&e_hdr, &zbf); res = _z_decl_token_decode(&d_qd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_token_declaration(&e_qd, &d_qd); printf("\n"); // Free _z_wireexpr_clear(&e_qd._keyexpr); _z_wireexpr_clear(&d_qd._keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Forget Resource declaration ------------------*/ _z_undecl_kexpr_t gen_forget_resource_declaration(void) { _z_undecl_kexpr_t e_frd; e_frd._id = gen_uint16(); return e_frd; } void assert_eq_forget_resource_declaration(const _z_undecl_kexpr_t *left, const _z_undecl_kexpr_t *right) { printf("RID (%u:%u)", left->_id, right->_id); assert(left->_id == right->_id); } void forget_resource_declaration(void) { printf("\n>> Forget resource declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_undecl_kexpr_t e_frd = gen_forget_resource_declaration(); // Encode z_result_t res = _z_undecl_kexpr_encode(&wbf, &e_frd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_undecl_kexpr_t d_frd; uint8_t hdr; _z_uint8_decode(&hdr, &zbf); res = _z_undecl_kexpr_decode(&d_frd, &zbf, hdr); assert(res == _Z_RES_OK); printf(" "); assert_eq_forget_resource_declaration(&e_frd, &d_frd); printf("\n"); // Free // NOTE: forget_res_decl does not involve any heap allocation _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Forget Subscriber declaration ------------------*/ _z_undecl_subscriber_t gen_forget_subscriber_declaration(void) { _z_undecl_subscriber_t e_fsd = {._ext_keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_uint64()}; return e_fsd; } void assert_eq_forget_subscriber_declaration(const _z_undecl_subscriber_t *left, const _z_undecl_subscriber_t *right) { assert_eq_keyexpr(&left->_ext_keyexpr, &right->_ext_keyexpr); assert(left->_id == right->_id); } void forget_subscriber_declaration(void) { printf("\n>> Forget subscriber declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_undecl_subscriber_t e_fsd = gen_forget_subscriber_declaration(); // Encode z_result_t res = _z_undecl_subscriber_encode(&wbf, &e_fsd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_undecl_subscriber_t d_fsd = {._id = 0, ._ext_keyexpr = {0}}; uint8_t e_hdr = 0; _z_uint8_decode(&e_hdr, &zbf); res = _z_undecl_subscriber_decode(&d_fsd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_forget_subscriber_declaration(&e_fsd, &d_fsd); printf("\n"); // Free _z_wireexpr_clear(&e_fsd._ext_keyexpr); _z_wireexpr_clear(&d_fsd._ext_keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Forget Queryable declaration ------------------*/ _z_undecl_queryable_t gen_forget_queryable_declaration(void) { _z_undecl_queryable_t e_fqd = {._ext_keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_zint()}; return e_fqd; } void assert_eq_forget_queryable_declaration(const _z_undecl_queryable_t *left, const _z_undecl_queryable_t *right) { assert_eq_keyexpr(&left->_ext_keyexpr, &right->_ext_keyexpr); assert(left->_id == right->_id); } void forget_queryable_declaration(void) { printf("\n>> Forget queryable declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_undecl_queryable_t e_fqd = gen_forget_queryable_declaration(); // Encode z_result_t res = _z_undecl_queryable_encode(&wbf, &e_fqd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t e_hdr = 0; _z_uint8_decode(&e_hdr, &zbf); _z_undecl_queryable_t d_fqd = {._ext_keyexpr = _z_wireexpr_null()}; res = _z_undecl_queryable_decode(&d_fqd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_forget_queryable_declaration(&e_fqd, &d_fqd); printf("\n"); // Free _z_wireexpr_clear(&e_fqd._ext_keyexpr); _z_wireexpr_clear(&d_fqd._ext_keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Forget Token declaration ------------------*/ _z_undecl_token_t gen_forget_token_declaration(void) { _z_undecl_token_t e_fqd = {._ext_keyexpr = gen_wireexpr(), ._id = (uint32_t)gen_zint()}; return e_fqd; } void assert_eq_forget_token_declaration(const _z_undecl_token_t *left, const _z_undecl_token_t *right) { assert_eq_keyexpr(&left->_ext_keyexpr, &right->_ext_keyexpr); assert(left->_id == right->_id); } void forget_token_declaration(void) { printf("\n>> Forget token declaration\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_undecl_token_t e_fqd = gen_forget_token_declaration(); // Encode int8_t res = _z_undecl_token_encode(&wbf, &e_fqd); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t e_hdr = 0; _z_uint8_decode(&e_hdr, &zbf); _z_undecl_token_t d_fqd = {._ext_keyexpr = _z_wireexpr_null()}; res = _z_undecl_token_decode(&d_fqd, &zbf, e_hdr, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); printf(" "); assert_eq_forget_token_declaration(&e_fqd, &d_fqd); printf("\n"); // Free _z_wireexpr_clear(&e_fqd._ext_keyexpr); _z_wireexpr_clear(&d_fqd._ext_keyexpr); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Declaration ------------------*/ _z_declaration_t gen_declaration(void) { uint8_t decl[] = {_Z_DECL_KEXPR, _Z_UNDECL_KEXPR, _Z_DECL_SUBSCRIBER, _Z_UNDECL_SUBSCRIBER, _Z_DECL_QUERYABLE, _Z_UNDECL_QUERYABLE, _Z_DECL_TOKEN, _Z_UNDECL_TOKEN}; _z_declaration_t d; d._tag = decl[gen_uint8() % (sizeof(decl) / sizeof(uint8_t))]; switch (d._tag) { case _Z_DECL_KEXPR: { d._body._decl_kexpr = gen_resource_declaration(); } break; case _Z_UNDECL_KEXPR: { d._body._undecl_kexpr = gen_forget_resource_declaration(); } break; case _Z_DECL_SUBSCRIBER: { d._body._decl_subscriber = gen_subscriber_declaration(); } break; case _Z_UNDECL_SUBSCRIBER: { d._body._undecl_subscriber = gen_forget_subscriber_declaration(); } break; case _Z_DECL_QUERYABLE: { d._body._decl_queryable = gen_queryable_declaration(); } break; case _Z_UNDECL_QUERYABLE: { d._body._undecl_queryable = gen_forget_queryable_declaration(); } break; case _Z_DECL_TOKEN: { d._body._decl_token = gen_token_declaration(); } break; case _Z_UNDECL_TOKEN: { d._body._undecl_token = gen_forget_token_declaration(); } break; default: assert(false); } return d; } void assert_eq_declaration(const _z_declaration_t *left, const _z_declaration_t *right) { printf("Declaration -> "); printf("Header (%x:%x), ", left->_tag, right->_tag); assert(left->_tag == right->_tag); switch (left->_tag) { case _Z_DECL_KEXPR: assert_eq_resource_declaration(&left->_body._decl_kexpr, &right->_body._decl_kexpr); break; case _Z_DECL_SUBSCRIBER: assert_eq_subscriber_declaration(&left->_body._decl_subscriber, &right->_body._decl_subscriber); break; case _Z_DECL_QUERYABLE: assert_eq_queryable_declaration(&left->_body._decl_queryable, &right->_body._decl_queryable); break; case _Z_DECL_TOKEN: assert_eq_token_declaration(&left->_body._decl_token, &right->_body._decl_token); break; case _Z_UNDECL_KEXPR: assert_eq_forget_resource_declaration(&left->_body._undecl_kexpr, &right->_body._undecl_kexpr); break; case _Z_UNDECL_SUBSCRIBER: assert_eq_forget_subscriber_declaration(&left->_body._undecl_subscriber, &right->_body._undecl_subscriber); break; case _Z_UNDECL_QUERYABLE: assert_eq_forget_queryable_declaration(&left->_body._undecl_queryable, &right->_body._undecl_queryable); break; case _Z_UNDECL_TOKEN: assert_eq_forget_token_declaration(&left->_body._undecl_token, &right->_body._undecl_token); break; default: assert(false); } } /*------------------ Declare message ------------------*/ _z_network_message_t gen_declare_message(void) { _z_declaration_t declaration = gen_declaration(); bool has_id = gen_bool(); uint32_t id = gen_uint32(); _z_network_message_t n_msg; _z_n_msg_make_declare(&n_msg, declaration, has_id ? _z_optional_id_make_some(id) : _z_optional_id_make_none()); return n_msg; } void assert_eq_declare_message(_z_n_msg_declare_t *left, _z_n_msg_declare_t *right) { printf(" "); assert(left->_interest_id.has_value == right->_interest_id.has_value); if (left->_interest_id.has_value) { assert(left->_interest_id.value == right->_interest_id.value); } assert_eq_declaration(&left->_decl, &right->_decl); printf("\n"); } void declare_message(void) { printf("\n>> Declare message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_network_message_t n_msg = gen_declare_message(); // Encode z_result_t res = _z_network_message_encode(&wbf, &n_msg); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_network_message_t d_dcl = {0}; _z_arc_slice_t arcs = {0}; res = _z_network_message_decode(&d_dcl, &zbf, &arcs, _Z_KEYEXPR_MAPPING_LOCAL); assert(res == _Z_RES_OK); assert_eq_declare_message(&n_msg._body._declare, &d_dcl._body._declare); // Free _z_n_msg_clear(&d_dcl); _z_n_msg_clear(&n_msg); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*------------------ Interest ------------------*/ #define _Z_MSGCODEC_INTEREST_BASE_FLAGS_MASK 0x8f // Used to remove R, C & F flags _z_interest_t gen_interest(void) { _z_interest_t i = {0}; bool is_final = gen_bool(); // To determine if interest is final or not // Generate interest id i._id = gen_uint32(); printf("Gen interest %d\n", is_final); // Add regular interest data if (!is_final) { // Generate base flags i.flags = gen_uint8() & _Z_MSGCODEC_INTEREST_BASE_FLAGS_MASK; // Generate test cases bool is_restricted = gen_bool(); uint8_t cf_type = gen_uint8() % 3; // Flags must be c, f or cf switch (cf_type) { default: case 0: i.flags |= _Z_INTEREST_FLAG_CURRENT; break; case 1: i.flags |= _Z_INTEREST_FLAG_FUTURE; break; case 2: i.flags |= (_Z_INTEREST_FLAG_CURRENT | _Z_INTEREST_FLAG_FUTURE); break; } if (is_restricted) { i.flags |= _Z_INTEREST_FLAG_RESTRICTED; // Generate ke i._keyexpr = gen_wireexpr(); } }; return i; } _z_network_message_t gen_interest_message(void) { _z_interest_t interest = gen_interest(); _z_network_message_t msg; _z_n_msg_make_interest(&msg, interest); return msg; } void assert_eq_interest(const _z_interest_t *left, const _z_interest_t *right) { printf("Interest: 0x%x, 0x%x, %u, %u\n", left->flags, right->flags, left->_id, right->_id); printf("Interest ke: %d, %d, %d, %d, %.*s, %.*s\n", left->_keyexpr._id, right->_keyexpr._id, (unsigned int)left->_keyexpr._mapping, (unsigned int)right->_keyexpr._mapping, (int)_z_string_len(&left->_keyexpr._suffix), _z_string_data(&left->_keyexpr._suffix), (int)_z_string_len(&right->_keyexpr._suffix), _z_string_data(&right->_keyexpr._suffix)); assert(left->flags == right->flags); assert(left->_id == right->_id); assert_eq_keyexpr(&left->_keyexpr, &right->_keyexpr); } void interest_message(void) { printf("\n>> Interest message\n"); // Init _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_network_message_t expected = gen_interest_message(); // Encode assert(_z_n_interest_encode(&wbf, &expected._body._interest) == _Z_RES_OK); // Decode _z_n_msg_interest_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); assert(_z_n_interest_decode(&decoded, &zbf, header, _Z_KEYEXPR_MAPPING_LOCAL) == _Z_RES_OK); // Check assert_eq_interest(&expected._body._interest._interest, &decoded._interest); // Clean-up _z_n_msg_interest_clear(&decoded); _z_n_msg_interest_clear(&expected._body._interest); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } /*=============================*/ /* Zenoh Messages */ /*=============================*/ /*------------------ Push message ------------------*/ _z_push_body_t gen_push_body(void) { bool isput = gen_bool(); _z_timestamp_t ts = gen_bool() ? gen_timestamp() : _z_timestamp_null(); _z_source_info_t sinfo = gen_bool() ? gen_source_info() : _z_source_info_null(); _z_m_push_commons_t commons = {._source_info = sinfo, ._timestamp = ts}; if (isput) { return (_z_push_body_t){._is_put = true, ._body._put = { ._commons = commons, ._payload = gen_bytes(64), ._encoding = gen_encoding(), }}; } else { return (_z_push_body_t){._is_put = false, ._body._del = {._commons = commons}}; } } void assert_eq_push_body(const _z_push_body_t *left, const _z_push_body_t *right) { assert(left->_is_put == right->_is_put); if (left->_is_put) { assert_eq_bytes(&left->_body._put._payload, &right->_body._put._payload); assert_eq_encoding(&left->_body._put._encoding, &right->_body._put._encoding); assert_eq_timestamp(&left->_body._put._commons._timestamp, &right->_body._put._commons._timestamp); assert_eq_source_info(&left->_body._put._commons._source_info, &right->_body._put._commons._source_info); } else { assert_eq_timestamp(&left->_body._del._commons._timestamp, &right->_body._del._commons._timestamp); assert_eq_source_info(&left->_body._del._commons._source_info, &right->_body._del._commons._source_info); } } void push_body_message(void) { printf("\n>> Put/Del message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); // Initialize _z_push_body_t e_da = gen_push_body(); // Encode z_result_t res = _z_push_body_encode(&wbf, &e_da); assert(res == _Z_RES_OK); (void)(res); // Decode _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_push_body_t d_da = {0}; _z_arc_slice_t arcs = {0}; uint8_t header = _z_zbuf_read(&zbf); res = _z_push_body_decode(&d_da, &zbf, header, &arcs); assert(res == _Z_RES_OK); assert_eq_push_body(&e_da, &d_da); // Free _z_push_body_clear(&d_da); _z_push_body_clear(&e_da); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_msg_query_t gen_query(void) { return (_z_msg_query_t){ ._consolidation = (gen_uint8() % 4) - 1, ._ext_info = gen_source_info(), ._parameters = gen_slice(16), ._ext_value = gen_bool() ? gen_value() : _z_value_null(), }; } _z_msg_query_t gen_query_anyke(const char *parameters, bool _anyke) { return (_z_msg_query_t){ ._consolidation = (gen_uint8() % 4) - 1, ._ext_info = gen_source_info(), ._parameters = _z_slice_from_buf_custom_deleter( // SAFETY: only use null-terminated parameters in tests. // Flawfinder: ignore [CWE-126] (const uint8_t *)parameters, parameters == NULL ? 0 : strlen(parameters), _z_delete_context_static()), ._implicit_anyke = _anyke, ._ext_value = gen_bool() ? gen_value() : _z_value_null(), }; } void assert_eq_query(const _z_msg_query_t *left, const _z_msg_query_t *right) { assert(left->_consolidation == right->_consolidation); assert_eq_source_info(&left->_ext_info, &right->_ext_info); assert_eq_slice(&left->_parameters, &right->_parameters); assert_eq_value(&left->_ext_value, &right->_ext_value); } void query_message(void) { printf("\n>> Query message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_msg_query_t expected = gen_query(); assert(_z_query_encode(&wbf, &expected) == _Z_RES_OK); _z_msg_query_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t res = _z_query_decode(&decoded, &zbf, header); assert(_Z_RES_OK == res); assert_eq_query(&expected, &decoded); _z_msg_query_clear(&decoded); _z_msg_query_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void query_message_anyke(void) { printf("\n>> Query message _anyke\n"); const char *params[] = {NULL, "", "param1", "param2;_anyke", "param3;_anyke;param4", "_anyke;param5"}; for (size_t i = 0; i < sizeof(params) / sizeof(char *); i++) { for (size_t j = 0; j < 2; j++) { _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); bool anyke = j == 0; _z_msg_query_t expected = gen_query_anyke(params[i], anyke); assert(_z_query_encode(&wbf, &expected) == _Z_RES_OK); _z_msg_query_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t res = _z_query_decode(&decoded, &zbf, header); assert(_Z_RES_OK == res); if (anyke) { // SAFETY: only use null-terminated parameters in tests. // Flawfinder: ignore [CWE-126] size_t params_len = params[i] == NULL ? 0 : strlen(params[i]); assert(!decoded._implicit_anyke); // implicit _anyke is always false upon decoding, since _anyke should // be explicitly present in parameters assert(_z_parameters_has_anyke((const char *)decoded._parameters.start, decoded._parameters.len)); assert(params_len <= decoded._parameters.len); if (params_len > 0) { assert(strncmp(params[i], (const char *)decoded._parameters.start, params_len) == 0); } } else { assert_eq_query(&expected, &decoded); } _z_msg_query_clear(&decoded); _z_msg_query_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } } } _z_msg_err_t gen_err(void) { size_t len = 1 + gen_uint8(); return (_z_msg_err_t){ ._encoding = gen_encoding(), ._ext_source_info = gen_bool() ? gen_source_info() : _z_source_info_null(), ._payload = gen_payload(len), // Hangs if 0 }; } void assert_eq_err(const _z_msg_err_t *left, const _z_msg_err_t *right) { assert_eq_encoding(&left->_encoding, &right->_encoding); assert_eq_source_info(&left->_ext_source_info, &right->_ext_source_info); assert_eq_bytes(&left->_payload, &right->_payload); } void err_message(void) { printf("\n>> Err message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_msg_err_t expected = gen_err(); assert(_z_err_encode(&wbf, &expected) == _Z_RES_OK); _z_msg_err_t decoded = {0}; _z_arc_slice_t arcs = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); assert(_Z_RES_OK == _z_err_decode(&decoded, &zbf, header, &arcs)); assert_eq_err(&expected, &decoded); _z_msg_err_clear(&decoded); _z_msg_err_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_msg_reply_t gen_reply(void) { return (_z_msg_reply_t){ ._consolidation = (gen_uint8() % 4) - 1, ._body = gen_push_body(), }; } void assert_eq_reply(const _z_msg_reply_t *left, const _z_msg_reply_t *right) { assert(left->_consolidation == right->_consolidation); assert_eq_push_body(&left->_body, &right->_body); } void reply_message(void) { printf("\n>> Reply message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_msg_reply_t expected = gen_reply(); assert(_z_reply_encode(&wbf, &expected) == _Z_RES_OK); _z_msg_reply_t decoded = {0}; _z_arc_slice_t arcs = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); assert(_Z_RES_OK == _z_reply_decode(&decoded, &zbf, header, &arcs)); assert_eq_reply(&expected, &decoded); _z_msg_reply_clear(&decoded); _z_msg_reply_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_n_msg_push_t gen_push(void) { return (_z_n_msg_push_t){ ._body = gen_push_body(), ._key = gen_wireexpr(), ._qos = _z_n_qos_make(gen_bool(), gen_bool(), gen_uint8() % 8), ._timestamp = gen_timestamp(), }; } static _z_network_message_t gen_push_message(void) { return (_z_network_message_t){ ._tag = _Z_N_PUSH, ._body._push = gen_push(), }; } void assert_eq_push(const _z_n_msg_push_t *left, const _z_n_msg_push_t *right) { assert_eq_timestamp(&left->_timestamp, &right->_timestamp); assert_eq_keyexpr(&left->_key, &right->_key); assert(left->_qos._val == right->_qos._val); assert_eq_push_body(&left->_body, &right->_body); } void push_message(void) { printf("\n>> Push message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_n_msg_push_t expected = gen_push(); assert(_z_push_encode(&wbf, &expected) == _Z_RES_OK); _z_n_msg_push_t decoded = {0}; _z_arc_slice_t arcs = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); assert(_Z_RES_OK == _z_push_decode(&decoded, &zbf, header, &arcs, _Z_KEYEXPR_MAPPING_LOCAL)); assert_eq_push(&expected, &decoded); _z_n_msg_push_clear(&decoded); _z_n_msg_push_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_n_msg_request_t gen_request(void) { _z_qos_t qos_default = {._val = 5}; _z_n_msg_request_t request = { ._rid = gen_zint(), ._key = gen_wireexpr(), ._ext_qos = gen_bool() ? _z_n_qos_make(gen_bool(), gen_bool(), gen_uint8() % 8) : qos_default, ._ext_timestamp = gen_bool() ? gen_timestamp() : _z_timestamp_null(), ._ext_target = gen_uint8() % 3, ._ext_budget = gen_bool() ? (uint32_t)gen_uint64() : 0, ._ext_timeout_ms = gen_bool() ? gen_uint64() : 0, }; switch (gen_uint8() % 2) { case 0: { request._tag = _Z_REQUEST_QUERY; request._body._query = gen_query(); } break; case 1: default: { _z_push_body_t body = gen_push_body(); if (body._is_put) { request._tag = _Z_REQUEST_PUT; request._body._put = body._body._put; } else { request._tag = _Z_REQUEST_DEL; request._body._del = body._body._del; } } } return request; } static _z_network_message_t gen_request_message(void) { return (_z_network_message_t){ ._tag = _Z_N_REQUEST, ._body._request = gen_request(), }; } void assert_eq_request(const _z_n_msg_request_t *left, const _z_n_msg_request_t *right) { assert(left->_rid == right->_rid); assert_eq_keyexpr(&left->_key, &right->_key); assert(left->_ext_qos._val == right->_ext_qos._val); assert_eq_timestamp(&left->_ext_timestamp, &right->_ext_timestamp); assert(left->_ext_target == right->_ext_target); assert(left->_ext_budget == right->_ext_budget); assert(left->_ext_timeout_ms == right->_ext_timeout_ms); assert(left->_tag == right->_tag); switch (left->_tag) { case _Z_REQUEST_QUERY: { assert_eq_query(&left->_body._query, &right->_body._query); } break; case _Z_REQUEST_PUT: { assert_eq_push_body(&(_z_push_body_t){._is_put = true, ._body._put = left->_body._put}, &(_z_push_body_t){._is_put = true, ._body._put = right->_body._put}); } break; case _Z_REQUEST_DEL: { assert_eq_push_body(&(_z_push_body_t){._is_put = false, ._body._del = left->_body._del}, &(_z_push_body_t){._is_put = false, ._body._del = right->_body._del}); } break; } } void request_message(void) { printf("\n>> Request message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_n_msg_request_t expected = gen_request(); assert(_z_request_encode(&wbf, &expected) == _Z_RES_OK); _z_n_msg_request_t decoded = {0}; _z_arc_slice_t arcs = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t ret = _z_request_decode(&decoded, &zbf, header, &arcs, _Z_KEYEXPR_MAPPING_LOCAL); assert(_Z_RES_OK == ret); assert_eq_request(&expected, &decoded); _z_n_msg_request_clear(&decoded); _z_n_msg_request_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_n_msg_response_t gen_response(void) { _z_n_msg_response_t ret = { ._key = gen_wireexpr(), ._request_id = gen_zint(), ._ext_qos = _z_n_qos_make(gen_bool(), gen_bool(), gen_uint8() % 8), ._ext_timestamp = gen_bool() ? gen_timestamp() : _z_timestamp_null(), ._ext_responder = {._eid = gen_uint16(), ._zid = gen_zid()}, }; switch (gen_uint32() % 2) { case 0: { ret._tag = _Z_RESPONSE_BODY_ERR; ret._body._err = gen_err(); } break; case 1: default: { ret._tag = _Z_RESPONSE_BODY_REPLY; ret._body._reply = gen_reply(); } break; } return ret; } static _z_network_message_t gen_response_message(void) { return (_z_network_message_t){ ._tag = _Z_N_RESPONSE, ._body._response = gen_response(), }; } void assert_eq_response(const _z_n_msg_response_t *left, const _z_n_msg_response_t *right) { assert_eq_keyexpr(&left->_key, &right->_key); assert_eq_timestamp(&left->_ext_timestamp, &right->_ext_timestamp); assert(left->_request_id == right->_request_id); assert(left->_ext_qos._val == right->_ext_qos._val); assert(left->_ext_responder._eid == right->_ext_responder._eid); assert(memcmp(left->_ext_responder._zid.id, right->_ext_responder._zid.id, 16) == 0); assert(left->_tag == right->_tag); switch (left->_tag) { case _Z_RESPONSE_BODY_REPLY: { assert_eq_reply(&left->_body._reply, &right->_body._reply); } break; case _Z_RESPONSE_BODY_ERR: { assert_eq_err(&left->_body._err, &right->_body._err); } break; } } void response_message(void) { printf("\n>> Response message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_n_msg_response_t expected = gen_response(); assert(_z_response_encode(&wbf, &expected) == _Z_RES_OK); _z_n_msg_response_t decoded = {0}; _z_arc_slice_t arcs = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t ret = _z_response_decode(&decoded, &zbf, header, &arcs, _Z_KEYEXPR_MAPPING_LOCAL); assert(_Z_RES_OK == ret); assert_eq_response(&expected, &decoded); _z_n_msg_response_clear(&decoded); _z_n_msg_response_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_n_msg_response_final_t gen_response_final(void) { return (_z_n_msg_response_final_t){._request_id = gen_zint()}; } static _z_network_message_t gen_response_final_message(void) { return (_z_network_message_t){ ._tag = _Z_N_RESPONSE_FINAL, ._body._response_final = gen_response_final(), }; } void assert_eq_response_final(const _z_n_msg_response_final_t *left, const _z_n_msg_response_final_t *right) { assert(left->_request_id == right->_request_id); } void response_final_message(void) { printf("\n>> Request Final message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_n_msg_response_final_t expected = gen_response_final(); assert(_z_response_final_encode(&wbf, &expected) == _Z_RES_OK); _z_n_msg_response_final_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t ret = _z_response_final_decode(&decoded, &zbf, header); assert(_Z_RES_OK == ret); assert_eq_response_final(&expected, &decoded); _z_n_msg_response_final_clear(&decoded); _z_n_msg_response_final_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_n_msg_oam_t gen_oam(void) { _z_qos_t qos_default = {._val = 5}; _z_n_msg_oam_t oam = { ._id = gen_uint16(), ._ext_timestamp = gen_bool() ? gen_timestamp() : _z_timestamp_null(), ._ext_qos = gen_bool() ? _z_n_qos_make(gen_bool(), gen_bool(), gen_uint8() % 8) : qos_default, }; switch (gen_uint8() % 3) { case 0: { oam._enc = _Z_OAM_BODY_UNIT; } break; case 1: { oam._enc = _Z_OAM_BODY_ZINT; oam._body._zint._val = gen_zint(); } break; case 2: default: { oam._enc = _Z_OAM_BODY_ZBUF; oam._body._zbuf._val = gen_slice(gen_uint8()); } break; } return oam; } void assert_eq_oam(const _z_n_msg_oam_t *left, const _z_n_msg_oam_t *right) { assert(left->_id == right->_id); assert_eq_timestamp(&left->_ext_timestamp, &right->_ext_timestamp); assert(left->_ext_qos._val == right->_ext_qos._val); assert(left->_enc == right->_enc); switch (left->_enc) { case _Z_OAM_BODY_UNIT: break; ; case _Z_OAM_BODY_ZINT: assert(left->_body._zint._val == right->_body._zint._val); break; case _Z_OAM_BODY_ZBUF: assert_eq_slice(&left->_body._zbuf._val, &right->_body._zbuf._val); break; default: assert(false); } } void oam_message(void) { printf("\n>> OAM message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_n_msg_oam_t expected = gen_oam(); assert(_z_oam_encode(&wbf, &expected) == _Z_RES_OK); _z_n_msg_oam_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); uint8_t header = _z_zbuf_read(&zbf); z_result_t ret = _z_oam_decode(&decoded, &zbf, header); assert(_Z_RES_OK == ret); assert_eq_oam(&expected, &decoded); _z_n_msg_oam_clear(&decoded); _z_n_msg_oam_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_join(void) { _z_conduit_sn_list_t conduit = {._is_qos = gen_bool()}; if (conduit._is_qos) { for (int i = 0; i < Z_PRIORITIES_NUM; i++) { conduit._val._qos[i]._best_effort = gen_zint(); conduit._val._qos[i]._reliable = gen_zint(); } } else { conduit._val._plain._best_effort = gen_zint(); conduit._val._plain._reliable = gen_zint(); } return _z_t_msg_make_join(_z_whatami_from_uint8((gen_uint8() % 3)), gen_zint(), gen_zid(), conduit); } void assert_eq_join(const _z_t_msg_join_t *left, const _z_t_msg_join_t *right) { assert(memcmp(left->_zid.id, right->_zid.id, 16) == 0); assert(left->_lease == right->_lease); assert(left->_batch_size == right->_batch_size); assert(left->_whatami == right->_whatami); assert(left->_req_id_res == right->_req_id_res); assert(left->_seq_num_res == right->_seq_num_res); assert(left->_version == right->_version); assert(left->_next_sn._is_qos == right->_next_sn._is_qos); if (left->_next_sn._is_qos) { for (int i = 0; i < Z_PRIORITIES_NUM; i++) { assert(left->_next_sn._val._qos[i]._best_effort == right->_next_sn._val._qos[i]._best_effort); assert(left->_next_sn._val._qos[i]._reliable == right->_next_sn._val._qos[i]._reliable); } } else { assert(left->_next_sn._val._plain._best_effort == right->_next_sn._val._plain._best_effort); assert(left->_next_sn._val._plain._reliable == right->_next_sn._val._plain._reliable); } } void join_message(void) { printf("\n>> Join message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_join(); assert(_z_join_encode(&wbf, expected._header, &expected._body._join) == _Z_RES_OK); _z_t_msg_join_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_join_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_join(&expected._body._join, &decoded); _z_t_msg_join_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_init(void) { if (gen_bool()) { return _z_t_msg_make_init_syn(_z_whatami_from_uint8((gen_uint8() % 3)), gen_zid()); } else { return _z_t_msg_make_init_ack(_z_whatami_from_uint8((gen_uint8() % 3)), gen_zid(), gen_slice(16)); } } void assert_eq_init(const _z_t_msg_init_t *left, const _z_t_msg_init_t *right) { assert(left->_batch_size == right->_batch_size); assert(left->_req_id_res == right->_req_id_res); assert(left->_seq_num_res == right->_seq_num_res); assert_eq_slice(&left->_cookie, &right->_cookie); assert(memcmp(left->_zid.id, right->_zid.id, 16) == 0); assert(left->_version == right->_version); assert(left->_whatami == right->_whatami); } void init_message(void) { printf("\n>> Init message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_init(); assert(_z_init_encode(&wbf, expected._header, &expected._body._init) == _Z_RES_OK); _z_t_msg_init_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_init_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_init(&expected._body._init, &decoded); _z_t_msg_init_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_open(void) { if (gen_bool()) { return _z_t_msg_make_open_syn(gen_uint32(), gen_uint32(), gen_slice(16)); } else { return _z_t_msg_make_open_ack(gen_uint32(), gen_uint32()); } } void assert_eq_open(const _z_t_msg_open_t *left, const _z_t_msg_open_t *right) { assert_eq_slice(&left->_cookie, &right->_cookie); assert(left->_initial_sn == right->_initial_sn); assert(left->_lease == right->_lease); } void open_message(void) { printf("\n>> open message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_open(); assert(_z_open_encode(&wbf, expected._header, &expected._body._open) == _Z_RES_OK); _z_t_msg_open_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_open_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_open(&expected._body._open, &decoded); _z_t_msg_open_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_close(void) { return _z_t_msg_make_close(gen_uint8(), gen_bool()); } void assert_eq_close(const _z_t_msg_close_t *left, const _z_t_msg_close_t *right) { assert(left->_reason == right->_reason); } void close_message(void) { printf("\n>> close message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_close(); assert(_z_close_encode(&wbf, expected._header, &expected._body._close) == _Z_RES_OK); _z_t_msg_close_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_close_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_close(&expected._body._close, &decoded); _z_t_msg_close_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_keep_alive(void) { return _z_t_msg_make_keep_alive(); } void assert_eq_keep_alive(const _z_t_msg_keep_alive_t *left, const _z_t_msg_keep_alive_t *right) { (void)left; (void)right; } void keep_alive_message(void) { printf("\n>> keep_alive message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_keep_alive(); assert(_z_keep_alive_encode(&wbf, expected._header, &expected._body._keep_alive) == _Z_RES_OK); _z_t_msg_keep_alive_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_keep_alive_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_keep_alive(&expected._body._keep_alive, &decoded); _z_t_msg_keep_alive_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_network_message_t gen_net_msg(void) { switch (gen_uint8() % 6) { default: case 0: { return gen_declare_message(); } break; case 1: { return (_z_network_message_t){._tag = _Z_N_PUSH, ._body._push = gen_push()}; } break; case 2: { return (_z_network_message_t){._tag = _Z_N_REQUEST, ._body._request = gen_request()}; } break; case 3: { return (_z_network_message_t){._tag = _Z_N_RESPONSE, ._body._response = gen_response()}; } break; case 4: { return (_z_network_message_t){._tag = _Z_N_RESPONSE_FINAL, ._body._response_final = gen_response_final()}; } break; case 5: { return (_z_network_message_t){._tag = _Z_N_INTEREST, ._body._interest._interest = gen_interest()}; } break; } } void assert_eq_net_msg(const _z_network_message_t *left, const _z_network_message_t *right) { assert(left->_tag == right->_tag); switch (left->_tag) { case _Z_N_DECLARE: { assert_eq_declaration(&left->_body._declare._decl, &right->_body._declare._decl); assert_eq_timestamp(&left->_body._declare._ext_timestamp, &right->_body._declare._ext_timestamp); assert(left->_body._declare._ext_qos._val == right->_body._declare._ext_qos._val); } break; case _Z_N_PUSH: { assert_eq_push(&left->_body._push, &right->_body._push); } break; case _Z_N_REQUEST: { assert_eq_request(&left->_body._request, &right->_body._request); } break; case _Z_N_RESPONSE: { assert_eq_response(&left->_body._response, &right->_body._response); } break; case _Z_N_RESPONSE_FINAL: { assert_eq_response_final(&left->_body._response_final, &right->_body._response_final); } break; case _Z_N_INTEREST: { assert_eq_interest(&left->_body._interest._interest, &right->_body._interest._interest); } break; default: assert(false); break; } } _z_network_message_svec_t gen_net_msgs(size_t n) { _z_network_message_svec_t ret = _z_network_message_svec_make(n); for (size_t i = 0; i < n; i++) { _z_network_message_t msg = gen_net_msg(); assert(_z_network_message_svec_append(&ret, &msg, false) == _Z_RES_OK); } return ret; } _z_transport_message_t gen_frame(_z_wbuf_t *wbf, _z_zbuf_t *zbf, _z_network_message_svec_t *nmsgs) { // Generate payload for (size_t i = 0; i < _z_network_message_svec_len(nmsgs); i++) { _z_network_message_t *msg = _z_network_message_svec_get(nmsgs, i); assert(_z_network_message_encode(wbf, msg) == _Z_RES_OK); } *zbf = _z_wbuf_to_zbuf(wbf); return _z_t_msg_make_frame(gen_uint32(), zbf, gen_bool()); } void assert_eq_frame(_z_network_message_svec_t *nmsgs, _z_t_msg_frame_t *left, _z_t_msg_frame_t *right) { assert(left->_sn == right->_sn); for (size_t i = 0; i < _z_network_message_svec_len(nmsgs); i++) { _z_network_message_t *expected = _z_network_message_svec_get(nmsgs, i); _z_network_message_t received = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); assert(_z_network_message_decode(&received, right->_payload, &arcs, _Z_KEYEXPR_MAPPING_LOCAL) == _Z_RES_OK); assert_eq_net_msg(expected, &received); _z_n_msg_clear(&received); } } void frame_message(void) { printf("\n>> frame message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_wbuf_t tmp_wbf = gen_wbuf(UINT16_MAX); _z_zbuf_t tmp_zbf = _z_zbuf_null(); _z_network_message_svec_t nmsgs = gen_net_msgs(1); _z_transport_message_t expected = gen_frame(&tmp_wbf, &tmp_zbf, &nmsgs); assert(_z_frame_encode(&wbf, expected._header, &expected._body._frame) == _Z_RES_OK); _z_t_msg_frame_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); assert(_z_frame_decode(&decoded, &zbf, expected._header) == _Z_RES_OK); assert_eq_frame(&nmsgs, &expected._body._frame, &decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); _z_wbuf_clear(&tmp_wbf); _z_zbuf_clear(&tmp_zbf); _z_network_message_svec_clear(&nmsgs); } _z_transport_message_t gen_fragment(void) { return _z_t_msg_make_fragment(gen_uint32(), gen_slice(gen_uint8()), gen_bool(), gen_bool(), gen_bool(), gen_bool()); } void assert_eq_fragment(const _z_t_msg_fragment_t *left, const _z_t_msg_fragment_t *right) { assert(left->_sn == right->_sn); assert_eq_slice(&left->_payload, &right->_payload); } void fragment_message(void) { printf("\n>> fragment message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_fragment(); assert(_z_fragment_encode(&wbf, expected._header, &expected._body._fragment) == _Z_RES_OK); _z_t_msg_fragment_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_fragment_decode(&decoded, &zbf, expected._header); assert(_Z_RES_OK == ret); assert_eq_fragment(&expected._body._fragment, &decoded); _z_t_msg_fragment_clear(&decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_transport_message_t gen_transport(void) { switch (gen_uint8() % 5) { case 0: { return gen_join(); }; case 1: { return gen_init(); }; case 2: { return gen_open(); }; case 3: { return gen_close(); }; default: case 4: { return gen_keep_alive(); }; } } void assert_eq_transport(const _z_transport_message_t *left, const _z_transport_message_t *right) { assert(left->_header == right->_header); switch (_Z_MID(left->_header)) { case _Z_MID_T_JOIN: { assert_eq_join(&left->_body._join, &right->_body._join); } break; case _Z_MID_T_CLOSE: { assert_eq_close(&left->_body._close, &right->_body._close); } break; case _Z_MID_T_INIT: { assert_eq_init(&left->_body._init, &right->_body._init); } break; case _Z_MID_T_OPEN: { assert_eq_open(&left->_body._open, &right->_body._open); } break; case _Z_MID_T_KEEP_ALIVE: { assert_eq_keep_alive(&left->_body._keep_alive, &right->_body._keep_alive); } break; default: assert(false); } } void transport_message(void) { printf("\n>> transport message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_transport_message_t expected = gen_transport(); assert(_z_transport_message_encode(&wbf, &expected) == _Z_RES_OK); _z_transport_message_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_transport_message_decode(&decoded, &zbf); assert(_Z_RES_OK == ret); assert_eq_transport(&expected, &decoded); _z_t_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } _z_scouting_message_t gen_scouting(void) { if (gen_bool()) { return _z_s_msg_make_scout((z_what_t)_z_whatami_from_uint8((gen_uint8() % 3)), gen_zid()); } else { return _z_s_msg_make_hello(_z_whatami_from_uint8((gen_uint8() % 3)), gen_zid(), gen_locator_array((gen_uint8() % 16) + 1)); } } void assert_eq_scouting(const _z_scouting_message_t *left, const _z_scouting_message_t *right) { assert(left->_header == right->_header); switch (_Z_MID(left->_header)) { case _Z_MID_SCOUT: { assert(left->_body._scout._version == right->_body._scout._version); assert(left->_body._scout._what == right->_body._scout._what); assert(memcmp(left->_body._scout._zid.id, right->_body._scout._zid.id, 16) == 0); } break; case _Z_MID_HELLO: { assert(left->_body._hello._version == right->_body._hello._version); assert(left->_body._hello._whatami == right->_body._hello._whatami); assert(memcmp(left->_body._hello._zid.id, right->_body._hello._zid.id, 16) == 0); assert_eq_locator_array(&left->_body._hello._locators, &right->_body._hello._locators); } break; default: assert(false); } } void scouting_message(void) { printf("\n>> scouting message\n"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_scouting_message_t expected = gen_scouting(); assert(_z_scouting_message_encode(&wbf, &expected) == _Z_RES_OK); _z_scouting_message_t decoded = {0}; _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); z_result_t ret = _z_scouting_message_decode(&decoded, &zbf); assert(_Z_RES_OK == ret); assert_eq_scouting(&expected, &decoded); _z_s_msg_clear(&decoded); _z_s_msg_clear(&expected); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } void test_serialize_deserialize(void) { uint8_t src_data[] = "Test\0Payload\0Data"; size_t src_len = sizeof(src_data) - 1; uint8_t header = 0xAB; uint8_t serialized[256] = {0}; uint8_t deserialized[256] = {0}; uint8_t tmp_buf[256] = {0}; size_t serialized_len = _z_serial_msg_serialize(serialized, sizeof(serialized), src_data, src_len, header, tmp_buf, sizeof(tmp_buf)); assert(serialized_len > 0); uint8_t decoded_header = 0; size_t deserialized_len = _z_serial_msg_deserialize(serialized, serialized_len, deserialized, sizeof(deserialized), &decoded_header, tmp_buf, sizeof(tmp_buf)); assert(deserialized_len != SIZE_MAX); assert(decoded_header == header); assert(deserialized_len == src_len); assert(memcmp(src_data, deserialized, src_len) == 0); } void test_crc_mismatch(void) { uint8_t src_data[] = "Test\0Payload\0Data"; size_t src_len = sizeof(src_data) - 1; uint8_t header = 0xCD; uint8_t serialized[256] = {0}; uint8_t tmp_buf[256] = {0}; size_t serialized_len = _z_serial_msg_serialize(serialized, sizeof(serialized), src_data, src_len, header, tmp_buf, sizeof(tmp_buf)); assert(serialized_len != SIZE_MAX); serialized[serialized_len - 2] ^= 0xFF; uint8_t decoded_header = 0; uint8_t deserialized[256] = {0}; size_t deserialized_len = _z_serial_msg_deserialize(serialized, serialized_len, deserialized, sizeof(deserialized), &decoded_header, tmp_buf, sizeof(tmp_buf)); assert(deserialized_len == SIZE_MAX); } void test_buffer_too_small(void) { uint8_t src_data[] = "Test\0Payload\0Data"; size_t src_len = sizeof(src_data) - 1; uint8_t header = 0xEF; uint8_t serialized[256] = {0}; uint8_t tmp_buf[256] = {0}; size_t serialized_len = _z_serial_msg_serialize(serialized, sizeof(serialized), src_data, src_len, header, tmp_buf, sizeof(tmp_buf)); assert(serialized_len != SIZE_MAX); uint8_t decoded_header = 0; uint8_t deserialized[4] = {0}; // Too small size_t deserialized_len = _z_serial_msg_deserialize(serialized, serialized_len, deserialized, sizeof(deserialized), &decoded_header, tmp_buf, sizeof(tmp_buf)); assert(deserialized_len == SIZE_MAX); } static _z_network_message_t make_specific_net_msg(uint8_t which) { switch (which) { case 0: return gen_declare_message(); case 1: return gen_push_message(); case 2: return gen_request_message(); case 3: return gen_response_message(); case 4: return gen_response_final_message(); case 5: return gen_interest_message(); default: assert(false && "Invalid network message selector"); } return (_z_network_message_t){0}; } static const char *net_msg_name(uint8_t which) { switch (which) { case 0: return "DECLARE"; case 1: return "PUSH"; case 2: return "INTEREST"; case 3: return "RESPONSE"; case 4: return "RESPONSE_FINAL"; case 5: return "REQUEST"; default: return "???"; } } // Encode exactly 2 messages (A then B) into one buffer, then decode them sequentially reusing the SAME decoded object. static void network_message_decode_pair_reuse(uint8_t a, uint8_t b, bool check_contents) { printf("\n>> Pair %s(%u) -> %s(%u) (check: %s)\n", net_msg_name(a), a, net_msg_name(b), b, check_contents ? "true" : "false"); _z_wbuf_t wbf = gen_wbuf(UINT16_MAX); _z_network_message_t expected[2]; expected[0] = make_specific_net_msg(a); expected[1] = make_specific_net_msg(b); assert(_z_network_message_encode(&wbf, &expected[0]) == _Z_RES_OK); assert(_z_network_message_encode(&wbf, &expected[1]) == _Z_RES_OK); _z_zbuf_t zbf = _z_wbuf_to_zbuf(&wbf); _z_network_message_t decoded = {0}; _z_arc_slice_t arcs = _z_arc_slice_empty(); for (int i = 0; i < 2; i++) { _z_n_msg_clear(&decoded); z_result_t r = _z_network_message_decode(&decoded, &zbf, &arcs, _Z_KEYEXPR_MAPPING_LOCAL); assert(r == _Z_RES_OK); if (check_contents) { assert_eq_net_msg(&expected[i], &decoded); } } _z_n_msg_clear(&decoded); _z_n_msg_clear(&expected[0]); _z_n_msg_clear(&expected[1]); _z_zbuf_clear(&zbf); _z_wbuf_clear(&wbf); } // 6x6 matrix: test all A->B transitions, one invocation per pair static void network_message_decode_pairwise_matrix(bool check_contents, size_t itr) { enum { TYPES = 6 }; printf( "\n>> Network pairwise matrix " "(2 messages per run), check: %s, iterations: %zu\n", check_contents ? "true" : "false", itr); for (size_t it = 0; it < itr; it++) { printf("\n-- MATRIX ITERATION %zu --\n", it); for (uint8_t a = 0; a < TYPES; a++) { for (uint8_t b = 0; b < TYPES; b++) { network_message_decode_pair_reuse(a, b, check_contents); } } } } /*=============================*/ /* Main */ /*=============================*/ int main(void) { setvbuf(stdout, NULL, _IOLBF, 1024); for (unsigned int i = 0; i < RUNS; i++) { printf("\n\n== RUN %u", i); // Core zint(); // Message fields payload_field(); timestamp_field(); keyexpr_field(); source_info_field(); // Zenoh declarations resource_declaration(); subscriber_declaration(); queryable_declaration(); forget_resource_declaration(); forget_subscriber_declaration(); forget_queryable_declaration(); // Zenoh messages declare_message(); push_body_message(); query_message(); query_message_anyke(); err_message(); reply_message(); interest_message(); // Network messages push_message(); request_message(); response_message(); response_final_message(); oam_message(); // Transport messages join_message(); init_message(); open_message(); close_message(); keep_alive_message(); frame_message(); fragment_message(); transport_message(); // Scouting messages scouting_message(); } // Serial serialization test_serialize_deserialize(); test_crc_mismatch(); test_buffer_too_small(); // Ensure that we can decode a sequence of messages reusing the same decoded object network_message_decode_pairwise_matrix(false, 1000); network_message_decode_pairwise_matrix(true, 1000); return 0; } #if defined(_WIN32) || defined(WIN32) #pragma warning(pop) #endif ================================================ FILE: tests/z_multi_pubsub_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #if Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_SUBSCRIPTION == 1 #define TEST_SLEEP_MS 2000 #define NUM_MSGS 8 static void pub_put_str(const z_loaned_publisher_t *pub, const char *s) { z_owned_bytes_t payload; ASSERT_OK(z_bytes_copy_from_str(&payload, s)); ASSERT_OK(z_publisher_put(pub, z_move(payload), NULL)); } static bool try_recv_pub_id(const z_loaned_fifo_handler_sample_t *handler, int *out_pub_id) { z_owned_sample_t sample; z_internal_sample_null(&sample); if (z_try_recv(handler, &sample) != Z_OK) { return false; } ASSERT_TRUE(z_sample_kind(z_loan(sample)) == Z_SAMPLE_KIND_PUT); // Parse payload as string: expected "m=%d p=%d" z_owned_string_t s; z_internal_string_null(&s); ASSERT_OK(z_bytes_to_string(z_sample_payload(z_loan(sample)), &s)); const char *ptr = z_string_data(z_loan(s)); int m = -1, p = -1; if (ptr != NULL && sscanf(ptr, "m=%d p=%d", &m, &p) == 2) { *out_pub_id = p; } else { // Ignore unexpected payloads (e.g., from other tests/processes) *out_pub_id = -1; } z_drop(z_move(s)); z_drop(z_move(sample)); return true; } static bool all_seen_publishers(const bool *seen, int num_pubs) { for (int i = 0; i < num_pubs; i++) { if (!seen[i]) { return false; } } return true; } static size_t collect_seen_publishers(const z_loaned_fifo_handler_sample_t *handler, unsigned total_wait_ms, unsigned step_ms, bool *seen, int num_pubs) { size_t got = 0; unsigned waited = 0; while (waited < total_wait_ms && !all_seen_publishers(seen, num_pubs)) { int pub_id = -1; while (try_recv_pub_id(handler, &pub_id)) { got++; if (pub_id >= 0 && pub_id < num_pubs) { seen[pub_id] = true; } } z_sleep_ms(step_ms); waited += step_ms; } // Final drain of what's currently available int pub_id = -1; while (try_recv_pub_id(handler, &pub_id)) { got++; if (pub_id >= 0 && pub_id < num_pubs) { seen[pub_id] = true; } } return got; } static void test_multi_pub_multi_sub(int num_pubs, int num_subs) { printf("test_multi_pub_multi_sub (pubs=%d subs=%d)\n", num_pubs, num_subs); const char *expr = "zenoh-pico/multi-pubsub"; z_owned_session_t s_pub, s_sub; z_owned_config_t c_pub, c_sub; z_config_default(&c_pub); z_config_default(&c_sub); z_view_keyexpr_t k; ASSERT_OK(z_view_keyexpr_from_str(&k, expr)); ASSERT_OK(z_open(&s_pub, z_config_move(&c_pub), NULL)); ASSERT_OK(z_open(&s_sub, z_config_move(&c_sub), NULL)); // Declare N publishers on s_pub z_owned_publisher_t *pubs = (z_owned_publisher_t *)z_malloc((size_t)num_pubs * sizeof(z_owned_publisher_t)); ASSERT_NOT_NULL(pubs); z_publisher_options_t pub_opts; z_publisher_options_default(&pub_opts); for (int i = 0; i < num_pubs; i++) { ASSERT_OK(z_declare_publisher(z_loan(s_pub), &pubs[i], z_loan(k), &pub_opts)); } // Declare M subscribers on s_sub, each with its own fifo channel z_owned_subscriber_t *subs = (z_owned_subscriber_t *)z_malloc((size_t)num_subs * sizeof(z_owned_subscriber_t)); z_owned_fifo_handler_sample_t *handlers = (z_owned_fifo_handler_sample_t *)z_malloc((size_t)num_subs * sizeof(z_owned_fifo_handler_sample_t)); ASSERT_NOT_NULL(subs); ASSERT_NOT_NULL(handlers); z_subscriber_options_t sub_opts; z_subscriber_options_default(&sub_opts); size_t fifo_capacity = (size_t)num_pubs * NUM_MSGS; for (int i = 0; i < num_subs; i++) { z_owned_closure_sample_t closure; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handlers[i], fifo_capacity)); ASSERT_OK(z_declare_subscriber(z_loan(s_sub), &subs[i], z_loan(k), z_move(closure), &sub_opts)); } // Allow subscription declarations to propagate z_sleep_ms(TEST_SLEEP_MS); // Publish multiple messages (from all publishers) to reduce flakiness. char msg[64]; for (int m = 0; m < NUM_MSGS; m++) { for (int p = 0; p < num_pubs; p++) { snprintf(msg, sizeof(msg), "m=%d p=%d", m, p); pub_put_str(z_loan(pubs[p]), msg); } // tiny pacing so we don't instantly overflow FIFOs at high fanout z_sleep_ms(5); } // Give time for delivery then assert each subscriber got >= 1 from each publisher for (int i = 0; i < num_subs; i++) { bool *seen = (bool *)z_malloc((size_t)num_pubs * sizeof(bool)); ASSERT_NOT_NULL(seen); size_t got = collect_seen_publishers(z_loan(handlers[i]), /*total_wait_ms=*/TEST_SLEEP_MS, /*step_ms=*/50, seen, num_pubs); if (!all_seen_publishers(seen, num_pubs)) { fprintf(stderr, "Subscriber %d missing samples from publishers:", i); for (int p = 0; p < num_pubs; p++) { if (!seen[p]) { fprintf(stderr, " %d", p); } } fprintf(stderr, " (total samples seen=%zu)\n", got); fflush(stderr); assert(false && "subscriber missing at least one publisher"); } z_free(seen); } // Cleanup for (int i = 0; i < num_subs; i++) { z_subscriber_drop(z_subscriber_move(&subs[i])); z_fifo_handler_sample_drop(z_fifo_handler_sample_move(&handlers[i])); } for (int i = 0; i < num_pubs; i++) { z_publisher_drop(z_publisher_move(&pubs[i])); } z_free(handlers); z_free(subs); z_free(pubs); z_session_drop(z_session_move(&s_pub)); z_session_drop(z_session_move(&s_sub)); } int main(int argc, char **argv) { (void)argc; (void)argv; test_multi_pub_multi_sub(1, 1); test_multi_pub_multi_sub(1, 4); test_multi_pub_multi_sub(1, 5); test_multi_pub_multi_sub(10, 10); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf( "Missing config token to build this test. This test requires: Z_FEATURE_PUBLICATION " "and Z_FEATURE_SUBSCRIPTION\n"); return 0; } #endif ================================================ FILE: tests/z_multi_queryable_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #if Z_FEATURE_QUERY == 1 && Z_FEATURE_QUERYABLE == 1 #define TEST_SLEEP_MS 2000 #define BASE_EXPR "zenoh-pico/multi-queryable" // Per-queryable state (kept alive for the lifetime of the queryable) typedef struct { int qid; z_owned_keyexpr_t ke; // concrete keyexpr for this queryable } qstate_t; static void on_query(z_loaned_query_t *query, void *ctx) { const qstate_t *st = (const qstate_t *)ctx; char msg[32]; (void)snprintf(msg, sizeof(msg), "q=%d", st->qid); z_owned_bytes_t payload; ASSERT_OK(z_bytes_copy_from_str(&payload, msg)); ASSERT_OK(z_query_reply(query, z_keyexpr_loan(&st->ke), z_move(payload), NULL)); } static bool all_seen(const bool *seen, int n) { for (int i = 0; i < n; i++) { if (!seen[i]) return false; } return true; } static bool try_recv_qid(const z_loaned_fifo_handler_reply_t *handler, int *out_qid) { z_owned_reply_t r; z_internal_reply_null(&r); if (z_try_recv(handler, &r) != Z_OK) { return false; } if (!z_reply_is_ok(z_loan(r))) { z_drop(z_move(r)); *out_qid = -1; return true; } const z_loaned_sample_t *sample = z_reply_ok(z_loan(r)); ASSERT_NOT_NULL(sample); z_owned_string_t s; z_internal_string_null(&s); ASSERT_OK(z_bytes_to_string(z_sample_payload(sample), &s)); const char *ptr = z_string_data(z_loan(s)); int qid = -1; if (ptr != NULL && sscanf(ptr, "q=%d", &qid) == 1) { *out_qid = qid; } else { *out_qid = -1; } z_drop(z_move(s)); z_drop(z_move(r)); return true; } static size_t collect_seen_queriables(const z_loaned_fifo_handler_reply_t *handler, unsigned total_wait_ms, unsigned step_ms, bool *seen, int num_q) { size_t got = 0; unsigned waited = 0; while (waited < total_wait_ms && !all_seen(seen, num_q)) { int qid = -1; while (try_recv_qid(handler, &qid)) { got++; if (qid >= 0 && qid < num_q) { seen[qid] = true; } } z_sleep_ms(step_ms); waited += step_ms; } // Final drain int qid = -1; while (try_recv_qid(handler, &qid)) { got++; if (qid >= 0 && qid < num_q) { seen[qid] = true; } } return got; } static void test_multi_queryables(int num_q) { printf("test_multi_queryables (queriables=%d)\n", num_q); z_owned_session_t s_q, s_cli; z_owned_config_t c_q, c_cli; z_config_default(&c_q); z_config_default(&c_cli); ASSERT_OK(z_open(&s_q, z_config_move(&c_q), NULL)); ASSERT_OK(z_open(&s_cli, z_config_move(&c_cli), NULL)); // Declare N queryables, each on its own keyexpr: BASE_EXPR/ z_owned_queryable_t *qs = (z_owned_queryable_t *)z_malloc((size_t)num_q * sizeof(z_owned_queryable_t)); qstate_t *states = (qstate_t *)z_malloc((size_t)num_q * sizeof(qstate_t)); ASSERT_NOT_NULL(qs); ASSERT_NOT_NULL(states); z_queryable_options_t qopts; z_queryable_options_default(&qopts); for (int i = 0; i < num_q; i++) { states[i].qid = i; z_internal_keyexpr_null(&states[i].ke); char expr[128]; (void)snprintf(expr, sizeof(expr), "%s/%d", BASE_EXPR, i); ASSERT_OK(z_keyexpr_from_str(&states[i].ke, expr)); z_owned_closure_query_t cb; ASSERT_OK(z_closure_query(&cb, on_query, NULL, &states[i])); ASSERT_OK(z_declare_queryable(z_loan(s_q), &qs[i], z_keyexpr_loan(&states[i].ke), z_move(cb), &qopts)); } // Let declarations propagate z_sleep_ms(TEST_SLEEP_MS); // Query with wildcard to match them all char qexpr[128]; (void)snprintf(qexpr, sizeof(qexpr), "%s/**", BASE_EXPR); z_view_keyexpr_t qke; ASSERT_OK(z_view_keyexpr_from_str(&qke, qexpr)); z_owned_fifo_handler_reply_t rh; z_owned_closure_reply_t rcb; // Capacity: expect at least num_q replies ASSERT_OK(z_fifo_channel_reply_new(&rcb, &rh, (size_t)num_q * 2)); z_get_options_t gopts; z_get_options_default(&gopts); gopts.timeout_ms = TEST_SLEEP_MS; gopts.target = Z_QUERY_TARGET_ALL; ASSERT_OK(z_get(z_loan(s_cli), z_loan(qke), "", z_closure_reply_move(&rcb), &gopts)); bool *seen = (bool *)z_malloc((size_t)num_q * sizeof(bool)); ASSERT_NOT_NULL(seen); (void)memset(seen, 0, (size_t)num_q * sizeof(bool)); size_t got = collect_seen_queriables(z_loan(rh), TEST_SLEEP_MS, 50, seen, num_q); if (!all_seen(seen, num_q)) { fprintf(stderr, "Missing replies from queryables:"); for (int i = 0; i < num_q; i++) { if (!seen[i]) fprintf(stderr, " %d", i); } fprintf(stderr, " (total replies seen=%zu)\n", got); fflush(stderr); assert(false && "missing at least one queryable reply"); } z_free(seen); // Cleanup z_fifo_handler_reply_drop(z_fifo_handler_reply_move(&rh)); for (int i = 0; i < num_q; i++) { z_queryable_drop(z_queryable_move(&qs[i])); z_keyexpr_drop(z_keyexpr_move(&states[i].ke)); } z_free(states); z_free(qs); z_session_drop(z_session_move(&s_q)); z_session_drop(z_session_move(&s_cli)); } int main(int argc, char **argv) { (void)argc; (void)argv; test_multi_queryables(1); test_multi_queryables(4); test_multi_queryables(5); test_multi_queryables(10); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf( "Missing config token to build this test. This test requires: " "Z_FEATURE_QUERY and Z_FEATURE_QUERYABLE\n"); return 0; } #endif ================================================ FILE: tests/z_open_test.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #if defined(ZENOH_WINDOWS) #define WIN32_LEAN_AND_MEAN #define _WINSOCKAPI_ #include #else #include #endif #include "utils/assert_helpers.h" #include "zenoh-pico.h" #include "zenoh-pico/api/macros.h" #include "zenoh-pico/api/primitives.h" #include "zenoh-pico/net/session.h" #include "zenoh-pico/transport/transport.h" #define OPEN_TEST_UNUSED_LOCATOR_1 "tcp/127.0.0.1:18101" #define OPEN_TEST_UNUSED_LOCATOR_2 "tcp/127.0.0.1:18102" #define OPEN_TEST_UNUSED_LOCATOR_3 "tcp/127.0.0.1:18103" #define OPEN_TEST_UNUSED_LOCATOR_4 "tcp/127.0.0.1:18104" #define OPEN_TEST_ALT_LOCATOR "tcp/127.0.0.1:18105" #define OPEN_TEST_MT_LOCATOR_1 "tcp/127.0.0.1:18111" #define OPEN_TEST_MT_LOCATOR_2 "tcp/127.0.0.1:18112" #define OPEN_TEST_MT_LOCATOR_3 "tcp/127.0.0.1:18113" #define OPEN_TEST_MT_LOCATOR_4 "tcp/127.0.0.1:18114" #define OPEN_TEST_MT_LOCATOR_5 "tcp/127.0.0.1:18115" #define OPEN_TEST_MT_LOCATOR_6 "tcp/127.0.0.1:18116" #define OPEN_TEST_MT_LOCATOR_7 "tcp/127.0.0.1:18117" #define OPEN_TEST_ST_LOCATOR_1 "tcp/127.0.0.1:18121" #define OPEN_TEST_ST_LOCATOR_2 "tcp/127.0.0.1:18122" #define OPEN_TEST_ST_LOCATOR_3 "tcp/127.0.0.1:18123" // Keep this conservative: busy CI runners may delay executor progress after z_open(). #define OPEN_TEST_LISTENER_SETTLE_MS 1000 /* * These OS thread helpers are test-harness concurrency only. They let the tests * exercise a single-threaded zenoh-pico build while another in-process session * is blocked in z_open(), and the main test thread can keep spinning listeners. */ #if defined(ZENOH_WINDOWS) typedef HANDLE open_test_task_t; typedef DWORD open_test_task_ret_t; #define OPEN_TEST_TASK_CALL WINAPI #define OPEN_TEST_TASK_RETURN 0 #else typedef pthread_t open_test_task_t; typedef void *open_test_task_ret_t; #define OPEN_TEST_TASK_CALL #define OPEN_TEST_TASK_RETURN NULL #endif typedef struct { z_owned_config_t config; z_owned_session_t session; uint32_t delay_ms; volatile bool done; z_result_t ret; } open_test_async_open_t; typedef struct { z_owned_session_t *session; volatile bool done; } open_test_async_spin_t; static z_result_t open_test_task_init(open_test_task_t *task, open_test_task_ret_t(OPEN_TEST_TASK_CALL *fun)(void *), void *arg) { #if defined(ZENOH_WINDOWS) *task = CreateThread(NULL, 0, fun, arg, 0, NULL); return (*task != NULL) ? _Z_RES_OK : _Z_ERR_GENERIC; #else return (pthread_create(task, NULL, fun, arg) == 0) ? _Z_RES_OK : _Z_ERR_GENERIC; #endif } static z_result_t open_test_task_join(open_test_task_t *task) { #if defined(ZENOH_WINDOWS) DWORD ret = WaitForSingleObject(*task, INFINITE); CloseHandle(*task); return (ret == WAIT_OBJECT_0) ? _Z_RES_OK : _Z_ERR_GENERIC; #else return (pthread_join(*task, NULL) == 0) ? _Z_RES_OK : _Z_ERR_GENERIC; #endif } static open_test_task_ret_t OPEN_TEST_TASK_CALL open_test_async_open_task(void *arg) { open_test_async_open_t *ctx = (open_test_async_open_t *)arg; z_sleep_ms(ctx->delay_ms); ctx->ret = z_open(&ctx->session, z_move(ctx->config), NULL); ctx->done = true; return OPEN_TEST_TASK_RETURN; } static void open_test_start_async_open(open_test_task_t *task, open_test_async_open_t *ctx, z_owned_config_t config, uint32_t delay_ms) { ctx->config = config; ctx->delay_ms = delay_ms; ctx->done = false; ctx->ret = _Z_ERR_GENERIC; ASSERT_OK(open_test_task_init(task, open_test_async_open_task, ctx)); } static void open_test_spin_once(z_owned_session_t *session) { #if Z_FEATURE_MULTI_THREAD == 0 (void)zp_spin_once(z_session_loan(session)); #else _ZP_UNUSED(session); #endif } #if Z_FEATURE_MULTI_THREAD == 0 && defined(Z_FEATURE_UNSTABLE_API) static open_test_task_ret_t OPEN_TEST_TASK_CALL open_test_async_spin_task(void *arg) { open_test_async_spin_t *ctx = (open_test_async_spin_t *)arg; while (!ctx->done) { open_test_spin_once(ctx->session); z_sleep_ms(10); } return OPEN_TEST_TASK_RETURN; } static void open_test_start_async_spin(open_test_task_t *task, open_test_async_spin_t *ctx, z_owned_session_t *session) { ctx->session = session; ctx->done = false; ASSERT_OK(open_test_task_init(task, open_test_async_spin_task, ctx)); } static void open_test_stop_async_spin(open_test_task_t *task, open_test_async_spin_t *ctx) { ctx->done = true; ASSERT_OK(open_test_task_join(task)); } #endif static size_t open_test_peer_count(z_owned_session_t *session) { _z_session_t *zs = _Z_RC_IN_VAL(&session->_rc); if (zs->_tp._type != _Z_TRANSPORT_UNICAST_TYPE) { return 0; } _z_transport_unicast_t *ztu = &zs->_tp._transport._unicast; _z_transport_peer_mutex_lock(&ztu->_common); size_t len = _z_transport_peer_unicast_slist_len(ztu->_peers); _z_transport_peer_mutex_unlock(&ztu->_common); return len; } static bool open_test_wait_for_peer_count(z_owned_session_t *target, size_t expected_count, z_owned_session_t **sessions, size_t session_count, uint32_t timeout_ms) { z_clock_t start = z_clock_now(); while (z_clock_elapsed_ms(&start) < timeout_ms) { if (open_test_peer_count(target) >= expected_count) { return true; } for (size_t i = 0; i < session_count; i++) { open_test_spin_once(sessions[i]); } z_sleep_ms(50); } return open_test_peer_count(target) >= expected_count; } static void open_test_settle_listener(z_owned_session_t **sessions, size_t session_count) { z_clock_t start = z_clock_now(); do { for (size_t i = 0; i < session_count; i++) { open_test_spin_once(sessions[i]); } z_sleep_ms(10); } while (z_clock_elapsed_ms(&start) < OPEN_TEST_LISTENER_SETTLE_MS); } static void open_test_wait_for_async_open(open_test_async_open_t *ctx, z_owned_session_t **sessions, size_t session_count, uint32_t timeout_ms) { z_clock_t start = z_clock_now(); while (!ctx->done && z_clock_elapsed_ms(&start) < timeout_ms) { for (size_t i = 0; i < session_count; i++) { open_test_spin_once(sessions[i]); } z_sleep_ms(10); } ASSERT_TRUE(ctx->done); } #if defined(Z_FEATURE_UNSTABLE_API) static void test_open_timeout_single_locator(void) { printf("Running test_open_timeout_single_locator() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_1); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); z_owned_session_t s; z_clock_t start = z_clock_now(); z_result_t ret = z_open(&s, z_move(c), NULL); unsigned long elapsed_ms = z_clock_elapsed_ms(&start); ASSERT_ERR(ret, _Z_ERR_TRANSPORT_OPEN_FAILED); ASSERT_TRUE(elapsed_ms >= 1000); } static void test_open_client_connect_exit_on_failure_false_still_requires_transport(void) { printf("Running test_open_client_connect_exit_on_failure_false_still_requires_transport() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "client"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_2); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY, "false"); z_owned_session_t s; z_clock_t start = z_clock_now(); z_result_t ret = z_open(&s, z_move(c), NULL); unsigned long elapsed_ms = z_clock_elapsed_ms(&start); ASSERT_ERR(ret, _Z_ERR_TRANSPORT_OPEN_FAILED); ASSERT_TRUE(elapsed_ms >= 1000); } static void test_open_invalid_timeout_value(void) { printf("Running test_open_invalid_timeout_value() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_3); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_TIMEOUT_KEY, "not-a-number"); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_INVALID_VALUE); } static void test_open_timeout_overflow_value(void) { printf("Running test_open_timeout_overflow_value() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_3); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_TIMEOUT_KEY, "2147483648"); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_INVALID_VALUE); } static void test_open_invalid_bool_value(void) { printf("Running test_open_invalid_bool_value() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_4); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY, "maybe"); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_INVALID_VALUE); } static void test_open_rejects_negative_connect_timeout_below_minus_one(void) { printf("Running test_open_rejects_negative_connect_timeout_below_minus_one() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_1); zp_config_insert(z_loan_mut(c), Z_CONFIG_CONNECT_TIMEOUT_KEY, "-2"); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_INVALID_VALUE); } static void test_open_rejects_negative_listen_timeout_below_minus_one(void) { printf("Running test_open_rejects_negative_listen_timeout_below_minus_one() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_LISTEN_KEY, OPEN_TEST_UNUSED_LOCATOR_2); zp_config_insert(z_loan_mut(c), Z_CONFIG_LISTEN_TIMEOUT_KEY, "-2"); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_INVALID_VALUE); } #endif static void test_open_multiple_listen_locators_are_rejected(void) { printf("Running test_open_multiple_listen_locators_are_rejected() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); _z_str_intmap_insert_push(z_loan_mut(c), Z_CONFIG_LISTEN_KEY, _z_str_clone(OPEN_TEST_UNUSED_LOCATOR_1)); _z_str_intmap_insert_push(z_loan_mut(c), Z_CONFIG_LISTEN_KEY, _z_str_clone(OPEN_TEST_UNUSED_LOCATOR_2)); z_owned_session_t s; ASSERT_ERR(z_open(&s, z_move(c), NULL), _Z_ERR_CONFIG_LOCATOR_INVALID); } static void test_open_peer_listen_succeeds(void) { printf("Running test_open_peer_listen_succeeds() ...\n"); z_owned_config_t c; z_config_default(&c); zp_config_insert(z_loan_mut(c), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c), Z_CONFIG_LISTEN_KEY, OPEN_TEST_ALT_LOCATOR); z_owned_session_t s; ASSERT_OK(z_open(&s, z_move(c), NULL)); z_drop(z_move(s)); } static void test_open_peer_uses_next_connect_locator_for_primary_transport(void) { printf("Running test_open_peer_uses_next_connect_locator_for_primary_transport() ...\n"); z_owned_config_t c1; z_owned_config_t c2; z_config_default(&c1); z_config_default(&c2); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, OPEN_TEST_ALT_LOCATOR); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_UNUSED_LOCATOR_1); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_ALT_LOCATOR); #if defined(Z_FEATURE_UNSTABLE_API) zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); #endif z_owned_session_t s1; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_settle_listener(listener_sessions, _ZP_ARRAY_SIZE(listener_sessions)); open_test_task_t task; open_test_async_open_t ctx; open_test_start_async_open(&task, &ctx, c2, 0); open_test_wait_for_async_open(&ctx, listener_sessions, _ZP_ARRAY_SIZE(listener_sessions), 3000); ASSERT_OK(open_test_task_join(&task)); ASSERT_OK(ctx.ret); z_owned_session_t *sessions[] = {&s1, &ctx.session}; ASSERT_TRUE(open_test_wait_for_peer_count(&s1, 1, sessions, _ZP_ARRAY_SIZE(sessions), 1000)); z_drop(z_move(ctx.session)); z_drop(z_move(s1)); } #if Z_FEATURE_UNICAST_PEER == 1 && defined(Z_FEATURE_UNSTABLE_API) static void _test_open_timeout_partial_connectivity(const char *connect_exit_on_failure, z_result_t expected_ret, const char *good_locator, const char *bad_locator) { z_owned_config_t c1; z_owned_config_t c2; z_config_default(&c1); z_config_default(&c2); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, good_locator); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, good_locator); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, bad_locator); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY, connect_exit_on_failure); z_owned_session_t s1; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_settle_listener(listener_sessions, _ZP_ARRAY_SIZE(listener_sessions)); z_clock_t start = z_clock_now(); open_test_task_t task; open_test_async_open_t ctx; open_test_start_async_open(&task, &ctx, c2, 0); open_test_wait_for_async_open(&ctx, listener_sessions, _ZP_ARRAY_SIZE(listener_sessions), 3000); unsigned long elapsed_ms = z_clock_elapsed_ms(&start); ASSERT_OK(open_test_task_join(&task)); z_result_t ret = ctx.ret; ASSERT_ERR(ret, expected_ret); if ((expected_ret == _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY) || (expected_ret == _Z_ERR_TRANSPORT_OPEN_FAILED)) { ASSERT_TRUE(elapsed_ms >= 1000); } if (ret == _Z_RES_OK) { ASSERT_TRUE(open_test_peer_count(&ctx.session) >= 1); z_drop(z_move(ctx.session)); } z_drop(z_move(s1)); } static void test_open_timeout_partial_connectivity_exit_on_failure_false(void) { printf("Running test_open_timeout_partial_connectivity_exit_on_failure_false() ...\n"); _test_open_timeout_partial_connectivity("false", _Z_RES_OK, OPEN_TEST_MT_LOCATOR_1, OPEN_TEST_MT_LOCATOR_2); } static void test_open_timeout_partial_connectivity_exit_on_failure_true(void) { printf("Running test_open_timeout_partial_connectivity_exit_on_failure_true() ...\n"); _test_open_timeout_partial_connectivity("true", _Z_ERR_TRANSPORT_OPEN_PARTIAL_CONNECTIVITY, OPEN_TEST_MT_LOCATOR_3, OPEN_TEST_MT_LOCATOR_4); } static void test_open_peer_listen_failure_strict_fails_before_connect(void) { printf("Running test_open_peer_listen_failure_strict_fails_before_connect() ...\n"); z_owned_config_t c1; z_config_default(&c1); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, OPEN_TEST_MT_LOCATOR_5); z_owned_session_t s1; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_settle_listener(listener_sessions, _ZP_ARRAY_SIZE(listener_sessions)); z_owned_config_t c2; z_config_default(&c2); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, OPEN_TEST_MT_LOCATOR_5); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY, "true"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_MT_LOCATOR_5); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); z_owned_session_t s2; ASSERT_ERR(z_open(&s2, z_move(c2), NULL), _Z_ERR_TRANSPORT_OPEN_FAILED); z_drop(z_move(s1)); } static void test_open_peer_listen_failure_can_fallback_to_connect(void) { printf("Running test_open_peer_listen_failure_can_fallback_to_connect() ...\n"); z_owned_config_t c1; z_config_default(&c1); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, OPEN_TEST_MT_LOCATOR_6); z_owned_session_t s1; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_settle_listener(listener_sessions, _ZP_ARRAY_SIZE(listener_sessions)); z_owned_config_t c2; z_config_default(&c2); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, OPEN_TEST_MT_LOCATOR_6); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_EXIT_ON_FAILURE_KEY, "false"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_MT_LOCATOR_6); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "1000"); open_test_task_t task; open_test_async_open_t ctx; open_test_start_async_open(&task, &ctx, c2, 0); open_test_wait_for_async_open(&ctx, listener_sessions, _ZP_ARRAY_SIZE(listener_sessions), 3000); ASSERT_OK(open_test_task_join(&task)); ASSERT_OK(ctx.ret); z_owned_session_t *sessions[] = {&s1, &ctx.session}; ASSERT_TRUE(open_test_wait_for_peer_count(&ctx.session, 1, sessions, _ZP_ARRAY_SIZE(sessions), 1000)); z_drop(z_move(ctx.session)); z_drop(z_move(s1)); } #endif #if Z_FEATURE_UNICAST_PEER == 1 && defined(Z_FEATURE_UNSTABLE_API) static void test_open_pending_peer_progresses_after_partial_connectivity(void) { printf("Running test_open_pending_peer_progresses_after_partial_connectivity() ...\n"); z_owned_config_t c1; z_owned_config_t c2; z_owned_config_t c3; z_config_default(&c1); z_config_default(&c2); z_config_default(&c3); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, OPEN_TEST_ST_LOCATOR_1); /* * s2 opens its own listen locator, so it has a primary transport and z_open() * can return without requiring the connect locators to complete immediately. * * The second connect locator is left pending until s3 starts listening. * This verifies that the background add-peers task completes the pending * connection after z_open() has accepted partial connectivity. */ zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, OPEN_TEST_ST_LOCATOR_2); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_ST_LOCATOR_1); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_ST_LOCATOR_3); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "5000"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_EXIT_ON_FAILURE_KEY, "false"); zp_config_insert(z_loan_mut(c3), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c3), Z_CONFIG_LISTEN_KEY, OPEN_TEST_ST_LOCATOR_3); z_owned_session_t s1; z_owned_session_t s3; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_settle_listener(listener_sessions, _ZP_ARRAY_SIZE(listener_sessions)); open_test_task_t task; open_test_async_open_t ctx; open_test_start_async_open(&task, &ctx, c2, 0); open_test_wait_for_async_open(&ctx, listener_sessions, _ZP_ARRAY_SIZE(listener_sessions), 7000); ASSERT_OK(open_test_task_join(&task)); ASSERT_OK(ctx.ret); z_owned_session_t *initial_sessions[] = {&s1, &ctx.session}; ASSERT_TRUE( open_test_wait_for_peer_count(&ctx.session, 1, initial_sessions, _ZP_ARRAY_SIZE(initial_sessions), 1000)); ASSERT_OK(z_open(&s3, z_move(c3), NULL)); z_owned_session_t *late_listener_sessions[] = {&s1, &s3}; open_test_settle_listener(late_listener_sessions, _ZP_ARRAY_SIZE(late_listener_sessions)); #if Z_FEATURE_MULTI_THREAD == 0 open_test_task_t spin_task; open_test_async_spin_t spin_ctx; open_test_start_async_spin(&spin_task, &spin_ctx, &ctx.session); #endif z_owned_session_t *sessions[] = {&s1, &s3}; ASSERT_TRUE(open_test_wait_for_peer_count(&ctx.session, 2, sessions, _ZP_ARRAY_SIZE(sessions), 5000)); #if Z_FEATURE_MULTI_THREAD == 0 open_test_stop_async_spin(&spin_task, &spin_ctx); #endif z_drop(z_move(s3)); z_drop(z_move(ctx.session)); z_drop(z_move(s1)); } #endif #if defined(Z_FEATURE_UNSTABLE_API) static void test_open_late_joining_endpoint(void) { printf("Running test_open_late_joining_endpoint() ...\n"); z_owned_config_t c1; z_owned_config_t c2; z_config_default(&c1); z_config_default(&c2); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, OPEN_TEST_MT_LOCATOR_7); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_KEY, OPEN_TEST_MT_LOCATOR_7); zp_config_insert(z_loan_mut(c2), Z_CONFIG_CONNECT_TIMEOUT_KEY, "5000"); open_test_task_t task; open_test_async_open_t ctx; z_clock_t start = z_clock_now(); open_test_start_async_open(&task, &ctx, c2, 0); z_sleep_ms(1000); z_owned_session_t s1; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); z_owned_session_t *listener_sessions[] = {&s1}; open_test_wait_for_async_open(&ctx, listener_sessions, _ZP_ARRAY_SIZE(listener_sessions), 5000); unsigned long elapsed_ms = z_clock_elapsed_ms(&start); ASSERT_OK(open_test_task_join(&task)); ASSERT_OK(ctx.ret); ASSERT_TRUE(elapsed_ms >= 1000); z_drop(z_move(ctx.session)); z_drop(z_move(s1)); } #endif int main(void) { #if defined(Z_FEATURE_UNSTABLE_API) test_open_timeout_single_locator(); test_open_client_connect_exit_on_failure_false_still_requires_transport(); test_open_invalid_timeout_value(); test_open_timeout_overflow_value(); test_open_invalid_bool_value(); test_open_rejects_negative_connect_timeout_below_minus_one(); test_open_rejects_negative_listen_timeout_below_minus_one(); #endif test_open_multiple_listen_locators_are_rejected(); test_open_peer_listen_succeeds(); test_open_peer_uses_next_connect_locator_for_primary_transport(); #if Z_FEATURE_UNICAST_PEER == 1 && defined(Z_FEATURE_UNSTABLE_API) test_open_timeout_partial_connectivity_exit_on_failure_false(); test_open_timeout_partial_connectivity_exit_on_failure_true(); test_open_peer_listen_failure_strict_fails_before_connect(); test_open_peer_listen_failure_can_fallback_to_connect(); test_open_pending_peer_progresses_after_partial_connectivity(); #endif #if defined(Z_FEATURE_UNSTABLE_API) test_open_late_joining_endpoint(); #endif return 0; } ================================================ FILE: tests/z_perf_rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico.h" typedef struct { volatile unsigned long count; unsigned long curr_len; z_clock_t start; } z_stats_t; static z_stats_t test_stats; static volatile bool test_end; #if Z_FEATURE_SUBSCRIPTION == 1 void z_stats_stop(z_stats_t *stats) { // Ignore default value if (stats->curr_len == 0) { return; } // Print values unsigned long elapsed_ms = z_clock_elapsed_ms(&stats->start); printf("End test for pkt len: %lu, msg nb: %lu, time ms: %lu\n", stats->curr_len, stats->count, elapsed_ms); stats->count = 0; } void on_sample(z_loaned_sample_t *sample, void *context) { z_stats_t *stats = (z_stats_t *)context; z_owned_slice_t value; z_bytes_to_slice(z_sample_payload(sample), &value); unsigned long data_len = (unsigned long)z_slice_len(z_loan(value)); if (stats->curr_len != data_len) { // End previous measurement z_stats_stop(stats); // Check for end packet stats->curr_len = data_len; if (data_len == 1) { test_end = true; return; } // Start new measurement printf("Starting test for pkt len: %lu\n", stats->curr_len); stats->start = z_clock_now(); } z_drop(z_move(value)); stats->count++; } int main(int argc, char **argv) { char *keyexpr = "test/thr"; const char *mode = NULL; char *llocator = NULL; char *clocator = NULL; (void)argv; // Check if peer or client mode if (argc > 1) { mode = "peer"; llocator = "udp/224.0.0.224:7447#iface=lo"; } else { mode = "client"; clocator = "tcp/127.0.0.1:7447"; } // Set config z_owned_config_t config; z_config_default(&config); if (mode != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); } if (llocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, llocator); } if (clocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, clocator); } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Declare Subscriber/resource z_owned_closure_sample_t callback; z_closure(&callback, on_sample, NULL, (void *)&test_stats); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, keyexpr); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to create subscriber.\n"); exit(-1); } // Listen until stopped printf("Start listening.\n"); while (!test_end) { } // Wait for everything to settle printf("End of test\n"); z_sleep_s(1); // Clean up z_drop(z_move(sub)); z_drop(z_move(s)); exit(0); } #else int main(void) { (void)test_stats; printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this test requires it.\n"); return -2; } #endif ================================================ FILE: tests/z_perf_tx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico.h" #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) #define TEST_DURATION_US 10000000 #if Z_FEATURE_PUBLICATION == 1 int send_packets(unsigned long pkt_len, z_owned_publisher_t *pub, uint8_t *value) { z_clock_t test_start = z_clock_now(); unsigned long elapsed_us = 0; while (elapsed_us < TEST_DURATION_US) { // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, pkt_len, NULL, NULL); z_publisher_put(z_loan(*pub), z_move(payload), NULL); elapsed_us = z_clock_elapsed_us(&test_start); } return 0; } int main(int argc, char **argv) { unsigned long len_array[] = {1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8}; // Biggest value first uint8_t *value = (uint8_t *)malloc(len_array[0]); memset(value, 1, len_array[0]); char *keyexpr = "test/thr"; const char *mode = NULL; char *llocator = NULL; char *clocator = NULL; (void)argv; // Check if peer or client mode if (argc > 1) { mode = "peer"; llocator = "udp/224.0.0.224:7447#iface=lo"; } else { mode = "client"; clocator = "tcp/127.0.0.1:7447"; } // Set config z_owned_config_t config; z_config_default(&config); if (mode != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); } if (llocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, llocator); } if (clocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, clocator); } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); exit(-1); } // Declare publisher z_owned_publisher_t pub; z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, keyexpr); if (z_declare_publisher(z_loan(s), &pub, z_loan(ke), NULL) < 0) { printf("Unable to declare publisher for key expression!\n"); exit(-1); } // Wait for joins if (strcmp(mode, "peer") == 0) { printf("Waiting for JOIN messages\n"); z_sleep_s(3); } // Send packets for (size_t i = 0; i < ARRAY_SIZE(len_array); i++) { printf("Start sending pkt len: %lu\n", len_array[i]); if (send_packets(len_array[i], &pub, value) != 0) { break; } } // Send end packet printf("Sending end pkt\n"); // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, 1, NULL, NULL); z_publisher_put(z_loan(pub), z_move(payload), NULL); // Clean up z_drop(z_move(pub)); z_drop(z_move(s)); free(value); exit(0); } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this test requires it.\n"); return -2; } #endif ================================================ FILE: tests/z_pqueue_test.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #undef NDEBUG #include // ── Instantiate int min-heap, capacity 8 ───────────────────────────────────── static inline int intpq_cmp(const int *a, const int *b) { return *a - *b; } #define _ZP_PQUEUE_TEMPLATE_ELEM_TYPE int #define _ZP_PQUEUE_TEMPLATE_NAME intpq #define _ZP_PQUEUE_TEMPLATE_SIZE 8 #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME intpq_cmp #include "zenoh-pico/collections/pqueue_template.h" // ── Instantiate int heap with context-carried comparator ───────────────────── // The context holds a multiplier: +1 → min-heap, -1 → max-heap. typedef struct { int multiplier; } intpq_cmp_ctx_t; static inline int intpq_with_ctx_cmp(const int *a, const int *b, const intpq_cmp_ctx_t *ctx) { return ctx->multiplier * (*a - *b); } #define _ZP_PQUEUE_TEMPLATE_ELEM_TYPE int #define _ZP_PQUEUE_TEMPLATE_NAME intpq_with_ctx #define _ZP_PQUEUE_TEMPLATE_SIZE 8 #define _ZP_PQUEUE_TEMPLATE_CMP_CTX_TYPE intpq_cmp_ctx_t #define _ZP_PQUEUE_TEMPLATE_ELEM_CMP_FN_NAME intpq_with_ctx_cmp #include "zenoh-pico/collections/pqueue_template.h" // ── Tests: context-free min-heap ───────────────────────────────────────────── static void test_new_is_empty(void) { printf("Test: new queue is empty\n"); intpq_t pq = intpq_new(); assert(intpq_is_empty(&pq)); assert(intpq_size(&pq) == 0); assert(intpq_peek(&pq) == NULL); intpq_destroy(&pq); } static void test_push_pop_single(void) { printf("Test: push then pop returns the same element\n"); intpq_t pq = intpq_new(); int v = 42; assert(intpq_push(&pq, &v)); assert(intpq_size(&pq) == 1); int out = 0; assert(intpq_pop(&pq, &out)); assert(out == 42); assert(intpq_is_empty(&pq)); intpq_destroy(&pq); } static void test_min_heap_order(void) { printf("Test: pop returns elements in ascending order\n"); intpq_t pq = intpq_new(); int vals[] = {5, 1, 8, 3, 2, 7, 4, 6}; for (int i = 0; i < 8; i++) { assert(intpq_push(&pq, &vals[i])); } assert(intpq_size(&pq) == 8); int prev = -1; for (int i = 0; i < 8; i++) { int out = 0; assert(intpq_pop(&pq, &out)); assert(out > prev); prev = out; } assert(intpq_is_empty(&pq)); intpq_destroy(&pq); } static void test_peek_does_not_remove(void) { printf("Test: peek returns min without removing it\n"); intpq_t pq = intpq_new(); int a = 10, b = 3, c = 7; assert(intpq_push(&pq, &a)); assert(intpq_push(&pq, &b)); assert(intpq_push(&pq, &c)); int *top = intpq_peek(&pq); assert(top != NULL && *top == 3); assert(intpq_size(&pq) == 3); // peek must not remove intpq_destroy(&pq); } static void test_capacity_exceeded(void) { printf("Test: push fails when capacity is full\n"); intpq_t pq = intpq_new(); for (int i = 0; i < 8; i++) { assert(intpq_push(&pq, &i)); } assert(intpq_size(&pq) == 8); int extra = 99; assert(!intpq_push(&pq, &extra)); // must fail assert(intpq_size(&pq) == 8); intpq_destroy(&pq); } static void test_pop_on_empty_returns_false(void) { printf("Test: pop on empty queue returns false\n"); intpq_t pq = intpq_new(); int out = 0; assert(!intpq_pop(&pq, &out)); intpq_destroy(&pq); } static void test_destroy_resets_size(void) { printf("Test: destroy resets size to zero\n"); intpq_t pq = intpq_new(); int v = 1; assert(intpq_push(&pq, &v)); intpq_destroy(&pq); assert(intpq_size(&pq) == 0); assert(intpq_is_empty(&pq)); } static void test_duplicate_values(void) { printf("Test: duplicate values are handled correctly\n"); intpq_t pq = intpq_new(); int vals[] = {3, 3, 1, 1, 2, 2}; for (int i = 0; i < 6; i++) { assert(intpq_push(&pq, &vals[i])); } int out, prev = -1; int count = 0; while (intpq_pop(&pq, &out)) { assert(out >= prev); prev = out; count++; } assert(count == 6); intpq_destroy(&pq); } static void test_push_pop_interleaved(void) { printf("Test: interleaved push and pop maintains heap property\n"); intpq_t pq = intpq_new(); // Push 3, pop min, push 1, pop min, etc. int vals[] = {5, 3, 8, 1, 4}; int expected[] = {3, 1, 4}; // after: push 5,3 pop→3; push 8,1 pop→1; push 4 pop→4 int v; v = 5; intpq_push(&pq, &v); v = 3; intpq_push(&pq, &v); int out = 0; assert(intpq_pop(&pq, &out) && out == expected[0]); v = 8; intpq_push(&pq, &v); v = 1; intpq_push(&pq, &v); assert(intpq_pop(&pq, &out) && out == expected[1]); v = 4; intpq_push(&pq, &v); assert(intpq_pop(&pq, &out) && out == expected[2]); (void)vals; intpq_destroy(&pq); } // ── Tests: context-aware (max-heap via multiplier = -1) ────────────────────── static void test_ctx_max_heap_order(void) { printf("Test (ctx): pop returns elements in descending order (max-heap)\n"); intpq_cmp_ctx_t ctx; ctx.multiplier = -1; intpq_with_ctx_t pq = intpq_with_ctx_new_with_ctx(&ctx); int vals[] = {5, 1, 8, 3, 2, 7, 4, 6}; for (int i = 0; i < 8; i++) { assert(intpq_with_ctx_push(&pq, &vals[i])); } int prev = 9, out = 0; for (int i = 0; i < 8; i++) { assert(intpq_with_ctx_pop(&pq, &out)); assert(out < prev); prev = out; } assert(intpq_with_ctx_is_empty(&pq)); intpq_with_ctx_destroy(&pq); } static void test_ctx_new_zero_init(void) { printf("Test (ctx): new() zero-initialises context pointer (min-heap behaviour with NULL ctx)\n"); // Context-free new() still works when CMP_CTX_TYPE is defined — // the context pointer is NULL. Since our compare never dereferences a // NULL ctx (it uses the multiplier which is 0 → treats all elements as // equal), the heap doesn't crash; we only verify it doesn't segfault and // that size/empty behave correctly. intpq_with_ctx_t pq = intpq_with_ctx_new(); assert(intpq_with_ctx_is_empty(&pq)); intpq_with_ctx_destroy(&pq); } static void test_ctx_set_ctx(void) { printf("Test (ctx): set_ctx switches comparison context on existing queue\n"); intpq_cmp_ctx_t min_ctx; min_ctx.multiplier = 1; intpq_cmp_ctx_t max_ctx; max_ctx.multiplier = -1; // Start as min-heap intpq_with_ctx_t pq = intpq_with_ctx_new_with_ctx(&min_ctx); int vals[] = {4, 2, 6}; for (int i = 0; i < 3; i++) intpq_with_ctx_push(&pq, &vals[i]); int out = 0; assert(intpq_with_ctx_pop(&pq, &out) && out == 2); // min first // Drain remaining, switch to max-heap, reload intpq_with_ctx_destroy(&pq); intpq_with_ctx_set_ctx(&pq, &max_ctx); for (int i = 0; i < 3; i++) intpq_with_ctx_push(&pq, &vals[i]); assert(intpq_with_ctx_pop(&pq, &out) && out == 6); // max first intpq_with_ctx_destroy(&pq); } int main(void) { // Context-free min-heap tests test_new_is_empty(); test_push_pop_single(); test_min_heap_order(); test_peek_does_not_remove(); test_capacity_exceeded(); test_pop_on_empty_returns_false(); test_destroy_resets_size(); test_duplicate_values(); test_push_pop_interleaved(); // Context-aware tests test_ctx_max_heap_order(); test_ctx_new_zero_init(); test_ctx_set_ctx(); printf("All pqueue tests passed.\n"); return 0; } ================================================ FILE: tests/z_refcount_test.c ================================================ // // Copyright (c) 2024 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/collections/refcount.h" #include "zenoh-pico/system/common/platform.h" #undef NDEBUG #include #define FOO_CLEARED_VALUE -1 typedef struct _dummy_t { int *foo; } _dummy_t; void _dummy_clear(_dummy_t *val) { if (val->foo != NULL) { *val->foo = FOO_CLEARED_VALUE; } return; } _Z_REFCOUNT_DEFINE(_dummy, _dummy) typedef struct { size_t _strong_cnt; size_t _weak_cnt; } _dummy_inner_rc_t; void test_rc_null(void) { _dummy_rc_t drc = _dummy_rc_null(); assert(drc._cnt == NULL); assert(drc._val == NULL); } void test_rc_size(void) { assert(_dummy_rc_size(NULL) == sizeof(_dummy_rc_t)); } void test_rc_drop(void) { _dummy_rc_t drc = _dummy_rc_null(); assert(!_dummy_rc_drop(NULL)); assert(!_dummy_rc_drop(&drc)); } void test_rc_new(void) { int val_foo = 42; _dummy_t *val = (_dummy_t *)z_malloc(sizeof(_dummy_t)); val->foo = &val_foo; _dummy_rc_t drc = _dummy_rc_new(val); assert(!_Z_RC_IS_NULL(&drc)); assert(_dummy_rc_strong_count(&drc) == 1); assert(_dummy_rc_weak_count(&drc) == 0); assert(*drc._val->foo == 42); *drc._val->foo = 0; assert(*val->foo == 0); assert(_dummy_rc_drop(&drc)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_new_from_val(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc = _dummy_rc_new_from_val(&val); assert(!_Z_RC_IS_NULL(&drc)); assert(_dummy_rc_strong_count(&drc) == 1); assert(_dummy_rc_weak_count(&drc) == 0); assert(*drc._val->foo == 42); *drc._val->foo = 0; assert(*val.foo == 0); assert(_dummy_rc_drop(&drc)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_new_undefined(void) { int val_foo = 42; _dummy_rc_t drc = _dummy_rc_new_undefined(); assert(!_Z_RC_IS_NULL(&drc)); assert(_dummy_rc_strong_count(&drc) == 1); assert(_dummy_rc_weak_count(&drc) == 0); _Z_RC_IN_VAL(&drc)->foo = &val_foo; assert(_dummy_rc_drop(&drc)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_clone(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); assert(_dummy_rc_strong_count(&drc1) == 1); assert(_dummy_rc_weak_count(&drc1) == 0); _dummy_rc_t drc2 = _dummy_rc_clone(&drc1); assert(!_Z_RC_IS_NULL(&drc2)); assert(_dummy_rc_strong_count(&drc2) == 2); assert(_dummy_rc_weak_count(&drc2) == 0); assert(_dummy_rc_strong_count(&drc2) == _z_rc_strong_count(drc1._cnt)); assert(_dummy_rc_weak_count(&drc2) == 0); assert(drc2._val->foo == drc1._val->foo); assert(!_dummy_rc_drop(&drc1)); assert(_dummy_rc_strong_count(&drc2) == 1); assert(_dummy_rc_weak_count(&drc2) == 0); assert(*drc2._val->foo == 42); assert(_dummy_rc_drop(&drc2)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_eq(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_rc_t drc2 = _dummy_rc_clone(&drc1); assert(_dummy_rc_eq(&drc1, &drc2)); assert(!_dummy_rc_drop(&drc1)); assert(_dummy_rc_drop(&drc2)); } void test_rc_clone_as_ptr(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_rc_t *drc2 = _dummy_rc_clone_as_ptr(&drc1); assert(drc2->_val != NULL); assert(!_Z_RC_IS_NULL(drc2)); assert(_dummy_rc_strong_count(drc2) == 2); assert(_dummy_rc_weak_count(drc2) == 0); assert(_dummy_rc_eq(&drc1, drc2)); assert(!_dummy_rc_drop(&drc1)); assert(_dummy_rc_drop(drc2)); z_free(drc2); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_copy(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_rc_t drc2 = _dummy_rc_null(); assert(!_dummy_rc_eq(&drc1, &drc2)); _dummy_rc_copy(&drc2, &drc1); assert(_dummy_rc_strong_count(&drc2) == 2); assert(_dummy_rc_weak_count(&drc2) == 0); assert(_dummy_rc_eq(&drc1, &drc2)); assert(!_dummy_rc_drop(&drc2)); assert(_dummy_rc_drop(&drc1)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_clone_as_weak(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_weak_t dwk1 = _dummy_rc_clone_as_weak(&drc1); assert(!_Z_RC_IS_NULL(&dwk1)); assert(_dummy_weak_strong_count(&dwk1) == 1); assert(_dummy_weak_weak_count(&dwk1) == 1); assert(*dwk1._val->foo == 42); assert(_dummy_rc_drop(&drc1)); assert(_dummy_weak_strong_count(&dwk1) == 0); assert(_dummy_weak_weak_count(&dwk1) == 1); assert(_dummy_weak_drop(&dwk1)); assert(val_foo == FOO_CLEARED_VALUE); } void test_rc_clone_as_weak_ptr(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_weak_t *dwk1 = _dummy_rc_clone_as_weak_ptr(&drc1); assert(dwk1 != NULL); assert(!_Z_RC_IS_NULL(dwk1)); assert(_dummy_weak_strong_count(dwk1) == 1); assert(_dummy_weak_weak_count(dwk1) == 1); assert(_dummy_rc_drop(&drc1)); assert(_dummy_weak_strong_count(dwk1) == 0); assert(_dummy_weak_weak_count(dwk1) == 1); assert(_dummy_weak_drop(dwk1)); z_free(dwk1); assert(val_foo == FOO_CLEARED_VALUE); } void test_weak_null(void) { _dummy_weak_t dwk = _dummy_weak_null(); assert(dwk._val == NULL); assert(dwk._cnt == NULL); } void test_weak_clone(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_weak_t dwk1 = _dummy_rc_clone_as_weak(&drc1); assert(_dummy_weak_strong_count(&dwk1) == 1); assert(_dummy_weak_weak_count(&dwk1) == 1); _dummy_weak_t dwk2 = _dummy_weak_clone(&dwk1); assert(_dummy_weak_strong_count(&dwk2) == 1); assert(_dummy_weak_weak_count(&dwk2) == 2); assert(_dummy_rc_drop(&drc1)); assert(_dummy_weak_strong_count(&dwk2) == 0); assert(_dummy_weak_weak_count(&dwk2) == 2); assert(val_foo == FOO_CLEARED_VALUE); assert(_dummy_weak_eq(&dwk1, &dwk2)); assert(!_dummy_weak_drop(&dwk2)); assert(_dummy_weak_drop(&dwk1)); } void test_weak_copy(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_weak_t dwk1 = _dummy_rc_clone_as_weak(&drc1); _dummy_weak_t dwk2 = _dummy_weak_null(); assert(!_dummy_weak_eq(&dwk1, &dwk2)); _dummy_weak_copy(&dwk2, &dwk1); assert(_dummy_weak_eq(&dwk1, &dwk2)); assert(_dummy_weak_strong_count(&dwk2) == 1); assert(_dummy_weak_weak_count(&dwk2) == 2); assert(!_dummy_weak_drop(&dwk1)); assert(!_dummy_weak_drop(&dwk2)); assert(_dummy_rc_drop(&drc1)); assert(val_foo == FOO_CLEARED_VALUE); } void test_weak_upgrade(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_weak_t dwk1 = _dummy_rc_clone_as_weak(&drc1); // Valid upgrade _dummy_rc_t drc2 = _dummy_weak_upgrade(&dwk1); assert(!_Z_RC_IS_NULL(&drc2)); assert(_dummy_rc_strong_count(&drc2) == 2); assert(_dummy_rc_weak_count(&drc2) == 1); assert(!_dummy_rc_drop(&drc1)); assert(_dummy_rc_drop(&drc2)); // Failed upgrade _dummy_rc_t drc3 = _dummy_weak_upgrade(&dwk1); assert(_Z_RC_IS_NULL(&drc3)); assert(_dummy_weak_strong_count(&dwk1) == 0); assert(_dummy_weak_weak_count(&dwk1) == 1); assert(_dummy_weak_drop(&dwk1)); assert(val_foo == FOO_CLEARED_VALUE); } void test_overflow(void) { _dummy_t val = {.foo = NULL}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); // Artificially set weak count to max value _dummy_inner_rc_t *dcnt = (_dummy_inner_rc_t *)drc1._cnt; dcnt->_strong_cnt = INT32_MAX; dcnt->_weak_cnt = INT32_MAX; _dummy_rc_t drc2 = _dummy_rc_clone(&drc1); assert(_Z_RC_IS_NULL(&drc2)); _dummy_weak_t dwk1 = _dummy_rc_clone_as_weak(&drc1); assert(_Z_RC_IS_NULL(&dwk1)); // Manual free to make asan happy, without long decresing free(drc1._val); free(drc1._cnt); } void test_decr(void) { _dummy_t val = {.foo = NULL}; _dummy_rc_t drc1 = _dummy_rc_new_from_val(&val); _dummy_rc_t drc2 = _dummy_rc_clone(&drc1); assert(!_dummy_rc_decr(&drc2)); assert(_dummy_rc_decr(&drc1)); free(drc1._val); } _Z_SIMPLE_REFCOUNT_DEFINE(_dummy, _dummy) void test_simple_rc_null(void) { _dummy_simple_rc_t drc = _dummy_simple_rc_null(); assert(drc._val == NULL); } void test_simple_rc_size(void) { assert(_dummy_simple_rc_size(NULL) == sizeof(_dummy_simple_rc_t)); } void test_simple_rc_drop(void) { _dummy_simple_rc_t drc = _dummy_simple_rc_null(); assert(!_dummy_simple_rc_drop(&drc)); } void test_simple_rc_new_from_val(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_simple_rc_t drc = _dummy_simple_rc_new_from_val(&val); assert(!_dummy_simple_rc_is_null(&drc)); assert(_z_simple_rc_strong_count(drc._val) == 1); assert(*_dummy_simple_rc_value(&drc)->foo == 42); assert(_dummy_simple_rc_drop(&drc)); assert(val_foo == FOO_CLEARED_VALUE); } void test_simple_rc_clone(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_simple_rc_t drc1 = _dummy_simple_rc_new_from_val(&val); assert(_z_simple_rc_strong_count(drc1._val) == 1); _dummy_simple_rc_t drc2 = _dummy_simple_rc_clone(&drc1); assert(!_dummy_simple_rc_is_null(&drc2)); assert(_z_simple_rc_strong_count(drc2._val) == 2); assert(_z_simple_rc_strong_count(drc2._val) == _z_simple_rc_strong_count(drc1._val)); assert(_dummy_simple_rc_value(&drc2)->foo == _dummy_simple_rc_value(&drc1)->foo); assert(!_dummy_simple_rc_drop(&drc1)); assert(_z_simple_rc_strong_count(drc2._val) == 1); assert(*_dummy_simple_rc_value(&drc2)->foo == 42); assert(_dummy_simple_rc_drop(&drc2)); assert(val_foo == FOO_CLEARED_VALUE); } void test_simple_rc_eq(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_simple_rc_t drc1 = _dummy_simple_rc_new_from_val(&val); _dummy_simple_rc_t drc2 = _dummy_simple_rc_clone(&drc1); assert(_dummy_simple_rc_eq(&drc1, &drc2)); assert(!_dummy_simple_rc_drop(&drc1)); assert(_dummy_simple_rc_drop(&drc2)); } void test_simple_rc_clone_as_ptr(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_simple_rc_t drc1 = _dummy_simple_rc_new_from_val(&val); _dummy_simple_rc_t *drc2 = _dummy_simple_rc_clone_as_ptr(&drc1); assert(drc2->_val != NULL); assert(!_dummy_simple_rc_is_null(drc2)); assert(_dummy_simple_rc_count(drc2) == 2); assert(_dummy_simple_rc_eq(&drc1, drc2)); assert(!_dummy_simple_rc_drop(&drc1)); assert(_dummy_simple_rc_count(drc2) == 1); assert(_dummy_simple_rc_drop(drc2)); z_free(drc2); assert(val_foo == FOO_CLEARED_VALUE); } void test_simple_rc_copy(void) { int val_foo = 42; _dummy_t val = {.foo = &val_foo}; _dummy_simple_rc_t drc1 = _dummy_simple_rc_new_from_val(&val); _dummy_simple_rc_t drc2 = _dummy_simple_rc_null(); assert(!_dummy_simple_rc_eq(&drc1, &drc2)); _dummy_simple_rc_copy(&drc2, &drc1); assert(_dummy_simple_rc_count(&drc2) == 2); assert(_dummy_simple_rc_eq(&drc1, &drc2)); assert(!_dummy_simple_rc_drop(&drc2)); assert(_dummy_simple_rc_drop(&drc1)); assert(val_foo == FOO_CLEARED_VALUE); } void test_simple_rc_decr(void) { _dummy_t val = {.foo = NULL}; _dummy_simple_rc_t drc1 = _dummy_simple_rc_new_from_val(&val); _dummy_simple_rc_t drc2 = _dummy_simple_rc_clone(&drc1); assert(!_dummy_simple_rc_decr(&drc2)); assert(_dummy_simple_rc_decr(&drc1)); // free manualy, to make asan happy, because counter already zero z_free(drc1._val); } void test_as_unsafe_ptr(void) { _dummy_t *val = (_dummy_t *)z_malloc(sizeof(_dummy_t)); val->foo = NULL; _dummy_rc_t drc = _dummy_rc_new(val); _dummy_weak_t dweak = _dummy_rc_clone_as_weak(&drc); assert(_dummy_weak_as_unsafe_ptr(&dweak) == val); assert(_dummy_rc_drop(&drc)); assert(_dummy_weak_drop(&dweak)); } void test_to_void(void) { _dummy_t *val = (_dummy_t *)z_malloc(sizeof(_dummy_t)); int val_foo = 42; val->foo = &val_foo; _dummy_rc_t drc = _dummy_rc_new(val); _z_void_rc_t void_rc = _dummy_rc_to_void(&drc); assert(drc._cnt == NULL); assert(drc._val == NULL); assert(_z_void_rc_strong_count(&void_rc) == 1); assert(val_foo == 42); assert(_z_void_rc_drop(&void_rc)); assert(val_foo == FOO_CLEARED_VALUE); } int main(void) { test_rc_null(); test_rc_size(); test_rc_drop(); test_rc_new(); test_rc_new_from_val(); test_rc_new_undefined(); test_rc_clone(); test_rc_eq(); test_rc_clone_as_ptr(); test_rc_copy(); test_rc_clone_as_weak(); test_rc_clone_as_weak_ptr(); test_weak_null(); test_weak_clone(); test_weak_copy(); test_weak_upgrade(); test_overflow(); test_decr(); test_as_unsafe_ptr(); test_simple_rc_null(); test_simple_rc_size(); test_simple_rc_drop(); test_simple_rc_new_from_val(); test_simple_rc_clone(); test_simple_rc_eq(); test_simple_rc_clone_as_ptr(); test_simple_rc_copy(); test_simple_rc_decr(); test_to_void(); return 0; } ================================================ FILE: tests/z_sync_group_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico.h" #undef NDEBUG #include #if Z_FEATURE_MULTI_THREAD == 1 typedef struct { _z_sync_group_notifier_t notifier1; _z_sync_group_notifier_t notifier2; size_t val; } wait_task_arg_t; void* sync_group_task(void* arg) { wait_task_arg_t* typed = (wait_task_arg_t*)arg; z_sleep_s(3); typed->val += 1; _z_sync_group_notifier_drop(&typed->notifier1); z_sleep_s(3); typed->val += 1; _z_sync_group_notifier_drop(&typed->notifier2); return NULL; } void test_sync_group_wait(void) { printf("test_sync_group_wait\n"); _z_sync_group_t g; assert(_z_sync_group_create(&g) == _Z_RES_OK); wait_task_arg_t arg; arg.val = 0; assert(_z_sync_group_create_notifier(&g, &arg.notifier1) == _Z_RES_OK); assert(_z_sync_group_create_notifier(&g, &arg.notifier2) == _Z_RES_OK); _z_task_t task; _z_task_init(&task, NULL, sync_group_task, &arg); assert(_z_sync_group_wait(&g) == _Z_RES_OK); assert(arg.val == 2); _z_sync_group_drop(&g); _z_task_join(&task); } void test_sync_group_wait_deadline(void) { printf("test_sync_group_wait_deadline\n"); _z_sync_group_t g; assert(_z_sync_group_create(&g) == Z_OK); wait_task_arg_t arg; arg.val = 0; assert(_z_sync_group_create_notifier(&g, &arg.notifier1) == Z_OK); assert(_z_sync_group_create_notifier(&g, &arg.notifier2) == Z_OK); _z_task_t task; _z_task_init(&task, NULL, sync_group_task, &arg); z_clock_t c = z_clock_now(); z_clock_advance_s(&c, 2); assert(_z_sync_group_wait_deadline(&g, &c) == Z_ETIMEDOUT); z_clock_advance_s(&c, 5); assert(_z_sync_group_wait_deadline(&g, &c) == Z_OK); assert(arg.val == 2); _z_sync_group_drop(&g); _z_task_join(&task); } #endif int main(void) { #if Z_FEATURE_MULTI_THREAD == 1 test_sync_group_wait(); test_sync_group_wait_deadline(); #endif return 0; } ================================================ FILE: tests/z_test_fragment_decode_error_transport_zbuf.c ================================================ // // Copyright (c) 2026 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "utils/assert_helpers.h" #include "zenoh-pico.h" #include "zenoh-pico/protocol/codec/transport.h" #include "zenoh-pico/transport/common/tx.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_FRAGMENTATION == 1 && \ Z_FEATURE_MULTI_THREAD == 0 #define MAX_READS 10 z_result_t _z_transport_message_encode_override(_z_wbuf_t *wbf, const _z_transport_message_t *msg, bool *handled) { z_result_t res = _Z_RES_OK; if (_Z_MID(msg->_header) == _Z_MID_T_FRAGMENT) { res = _z_fragment_encode(wbf, 0, &msg->_body._fragment); if (res < 0) { return res; } *handled = true; } return res; } static void dump_zbuf_state(const char *prefix, _z_zbuf_t *zbuf) { printf("%s rpos=%d wpos=%d capacity=%d len=%d left=%d\n", prefix, (int)_z_zbuf_get_rpos(zbuf), (int)_z_zbuf_get_wpos(zbuf), (int)_z_zbuf_capacity(zbuf), (int)_z_zbuf_len(zbuf), (int)_z_zbuf_space_left(zbuf)); } /** * Read until sample is received or error occurs. * * Returns: * _Z_RES_OK if a sample was received * Z_CHANNEL_NODATA if no sample was received and no read error occurred * <0 on zp_read()/z_try_recv() failure */ static z_result_t read_until_sample(const z_loaned_session_t *zs, const z_loaned_fifo_handler_sample_t *handler, _z_zbuf_t *zbuf, int max_reads) { z_result_t res = _Z_RES_OK; for (int i = 0; i < max_reads; i++) { res = zp_read(zs, NULL); if (res < 0) { printf("Failed to read from session: %d\n", res); dump_zbuf_state("[read error zbuf]", zbuf); return res; } z_owned_sample_t sample; res = z_try_recv(handler, &sample); if (res == Z_CHANNEL_NODATA) { printf("No sample received on read iteration %d.\n", i); dump_zbuf_state("[recv no data zbuf]", zbuf); } else if (res == _Z_RES_OK) { z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(z_loan(sample)), &keystr); printf("[rx] Received packet on %.*s, len: %zu\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), z_bytes_len(z_sample_payload(z_loan(sample)))); z_drop(z_move(sample)); dump_zbuf_state("[recv success zbuf]", zbuf); return res; } else { printf("Failed to receive sample: %d\n", res); dump_zbuf_state("[recv error zbuf]", zbuf); return res; } } return res; } static z_result_t publish_buf(const z_loaned_publisher_t *pub, uint8_t *value, size_t size) { z_owned_bytes_t payload; z_result_t res = z_bytes_from_buf(&payload, value, size, NULL, NULL); if (res < 0) { printf("Unable to create payload from buffer.\n"); return res; } res = z_publisher_put(pub, z_move(payload), NULL); if (res < 0) { printf("Failed to publish sample.\n"); return res; } return _Z_RES_OK; } int main(int argc, char **argv) { (void)argc; (void)argv; const char *keyexpr = "test/zenoh-pico-single-thread-fragment"; uint8_t *value = NULL; size_t size = 3000; value = z_malloc(size); ASSERT_NOT_NULL(value); for (size_t i = 0; i < size; i++) { value[i] = (uint8_t)i; } z_owned_config_t c1, c2; z_config_default(&c1); z_config_default(&c2); zp_config_insert(z_loan_mut(c1), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(c1), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(c2), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); z_owned_session_t s1, s2; ASSERT_OK(z_open(&s1, z_move(c1), NULL)); ASSERT_OK(z_open(&s2, z_move(c2), NULL)); z_owned_closure_sample_t closure; z_owned_fifo_handler_sample_t handler; ASSERT_OK(z_fifo_channel_sample_new(&closure, &handler, 3)); z_owned_subscriber_t sub; z_view_keyexpr_t ke; ASSERT_OK(z_view_keyexpr_from_str(&ke, keyexpr)); ASSERT_OK(z_declare_subscriber(z_loan(s1), &sub, z_loan(ke), z_move(closure), NULL)); z_owned_publisher_t pub; ASSERT_OK(z_declare_publisher(z_loan(s2), &pub, z_loan(ke), NULL)); z_sleep_s(1); printf("[tx]: Sending valid packet on %s, len: %d\n", keyexpr, (int)size); ASSERT_OK(publish_buf(z_loan(pub), value, size)); z_sleep_s(1); _z_session_t *session = _Z_RC_IN_VAL(z_loan(s1)); _z_zbuf_t *zbuf = &session->_tp._transport._multicast._common._zbuf; dump_zbuf_state("[initial zbuf]", zbuf); z_result_t res = read_until_sample(z_loan(s1), z_loan(handler), zbuf, MAX_READS); ASSERT_OK(res); dump_zbuf_state("[zbuf after sample]", zbuf); ASSERT_EQ_U32(_z_zbuf_get_rpos(zbuf), _z_zbuf_get_wpos(zbuf)); _z_transport_set_message_encode_override(_z_transport_message_encode_override); printf("[tx]: Sending corrupted packet on %s, len: %d\n", keyexpr, (int)size); ASSERT_OK(publish_buf(z_loan(pub), value, size)); z_sleep_s(1); res = read_until_sample(z_loan(s1), z_loan(handler), zbuf, MAX_READS); ASSERT_ERR(res, _Z_ERR_MESSAGE_TRANSPORT_UNKNOWN); dump_zbuf_state("[zbuf after bad sample]", zbuf); ASSERT_EQ_U32(_z_zbuf_get_rpos(zbuf), _z_zbuf_get_wpos(zbuf)); z_drop(z_move(sub)); z_drop(z_move(handler)); z_drop(z_move(pub)); z_drop(z_move(s1)); z_drop(z_move(s2)); z_free(value); return 0; } #else int main(void) { printf( "Missing config token to build this test. This test requires: Z_FEATURE_SUBSCRIPTION=1, " "Z_FEATURE_PUBLICATION=1, " "Z_FEATURE_FRAGMENTATION=1 and Z_FEATURE_MULTI_THREAD=0\n"); return 0; } #endif ================================================ FILE: tests/z_test_fragment_rx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #if Z_FEATURE_SUBSCRIPTION == 1 void data_handler(z_loaned_sample_t *sample, void *ctx) { (void)(ctx); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); bool is_valid = true; z_owned_slice_t value; z_bytes_to_slice(z_sample_payload(sample), &value); const uint8_t *data = z_slice_data(z_loan(value)); size_t data_len = z_slice_len(z_loan(value)); for (size_t i = 0; i < data_len; i++) { if (data[i] != (uint8_t)i) { is_valid = false; break; } } printf("[rx]: Received packet on %.*s, len: %d, validity: %d, qos {priority: %d, cong_ctrl: %d}\n", (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)data_len, is_valid, z_sample_priority(sample), z_sample_congestion_control(sample)); z_drop(z_move(value)); } int main(int argc, char **argv) { const char *keyexpr = "test/zenoh-pico-fragment"; const char *mode = NULL; char *llocator = NULL; char *clocator = NULL; (void)argv; // Check if peer mode if (argc > 1) { mode = "peer"; llocator = "udp/224.0.0.224:7447#iface=lo"; } else { mode = "client"; clocator = "tcp/127.0.0.1:7447"; } // Set config z_owned_config_t config; z_config_default(&config); if (mode != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); } if (llocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, llocator); } if (clocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, clocator); } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } // Declare subscriber z_owned_closure_sample_t callback; z_closure(&callback, data_handler, NULL, NULL); z_owned_subscriber_t sub; z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, keyexpr); if (z_declare_subscriber(z_loan(s), &sub, z_loan(ke), z_move(callback), NULL) < 0) { printf("Unable to declare subscriber.\n"); return -1; } // Wait for termination char c = '\0'; while (c != 'q') { fflush(stdin); int ret = scanf("%c", &c); (void)ret; // Remove unused result warning } // Clean up z_drop(z_move(sub)); z_drop(z_move(s)); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_SUBSCRIPTION but this test requires it.\n"); return -2; } #endif ================================================ FILE: tests/z_test_fragment_tx.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_PUBLICATION == 1 int main(int argc, char **argv) { const char *keyexpr = "test/zenoh-pico-fragment"; const char *mode = NULL; char *llocator = NULL; char *clocator = NULL; uint8_t *value = NULL; size_t size = 10000; (void)argv; // Init value value = malloc(size); if (value == NULL) { return -1; } for (size_t i = 0; i < size; i++) { value[i] = (uint8_t)i; } // Check if peer or client mode if (argc > 1) { mode = "peer"; llocator = "udp/224.0.0.224:7447#iface=lo"; } else { mode = "client"; clocator = "tcp/127.0.0.1:7447"; } // Set config z_owned_config_t config; z_config_default(&config); if (mode != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, mode); } if (llocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, llocator); } if (clocator != NULL) { zp_config_insert(z_loan_mut(config), Z_CONFIG_CONNECT_KEY, clocator); } // Open session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) < 0) { printf("Unable to open session!\n"); return -1; } // Wait for joins if (strcmp(mode, "peer") == 0) { z_sleep_s(3); } // Put data z_view_keyexpr_t ke; z_view_keyexpr_from_str(&ke, keyexpr); z_put_options_t options; z_put_options_default(&options); options.priority = Z_PRIORITY_DATA_HIGH; options.congestion_control = Z_CONGESTION_CONTROL_BLOCK; for (int i = 0; i < 5; i++) { // Create payload z_owned_bytes_t payload; z_bytes_from_buf(&payload, value, size, NULL, NULL); printf("[tx]: Sending packet on %s, len: %d\n", keyexpr, (int)size); if (z_put(z_loan(s), z_loan(ke), z_move(payload), &options) < 0) { printf("Oh no! Put has failed...\n"); return -1; } z_sleep_s(1); } // Clean up z_drop(z_move(s)); free(value); return 0; } #else int main(void) { printf("ERROR: Zenoh pico was compiled without Z_FEATURE_PUBLICATION but this test requires it.\n"); return -2; } #endif ================================================ FILE: tests/z_test_peer_multicast.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_QUERY == 1 && Z_FEATURE_QUERYABLE == 1 && \ Z_FEATURE_MULTI_THREAD == 1 && Z_FEATURE_LOCAL_SUBSCRIBER == 0 && defined Z_FEATURE_UNSTABLE_API typedef struct _node_ctx { z_owned_config_t config; const char *keyexpr_out; int id; int sub_msg_nb; int qybl_msg_nb; } _node_ctx_t; const char *keyexpr_in = "test/**"; const char *qybl_val = "Queryable data"; const char *pub_val = "Publisher data"; const int tx_nb = 5; const int rx_nb = 15; void query_handler(z_loaned_query_t *query, void *ctx) { _node_ctx_t *node_ctx = (_node_ctx_t *)ctx; z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable %d] Received ('%.*s': '%.*s')\n", node_ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Reply value z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, qybl_val); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); node_ctx->qybl_msg_nb++; } void pub_handler(z_loaned_sample_t *sample, void *ctx) { _node_ctx_t *node_ctx = (_node_ctx_t *)ctx; z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber %d] Received ('%.*s': '%.*s')\n", node_ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); node_ctx->sub_msg_nb++; } void *node_task(void *ptr) { _node_ctx_t *ctx = (_node_ctx_t *)ptr; // Open session printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(ctx->config), NULL) != Z_OK) { printf("Unable to open session!\n"); return NULL; } // Create keyexprs z_view_keyexpr_t sub_qybl_ke; if (z_view_keyexpr_from_str(&sub_qybl_ke, keyexpr_in) != Z_OK) { printf("%s is not a valid key expression\n", keyexpr_in); return NULL; } z_view_keyexpr_t pub_qry_ke; if (z_view_keyexpr_from_str(&pub_qry_ke, ctx->keyexpr_out) < 0) { printf("%s is not a valid key expression\n", ctx->keyexpr_out); return NULL; } // Declare subscriber printf("Declaring Subscriber on '%s'...\n", keyexpr_in); z_owned_closure_sample_t sub_cb; z_closure(&sub_cb, pub_handler, NULL, ctx); if (z_declare_background_subscriber(z_loan(s), z_loan(sub_qybl_ke), z_move(sub_cb), NULL) != Z_OK) { printf("Unable to declare subscriber.\n"); return NULL; } // Declare queryable printf("Creating Queryable on '%s'...\n", keyexpr_in); z_owned_closure_query_t qybl_cb; z_closure(&qybl_cb, query_handler, NULL, ctx); if (z_declare_background_queryable(z_loan(s), z_loan(sub_qybl_ke), z_move(qybl_cb), NULL) != Z_OK) { printf("Unable to create queryable.\n"); return NULL; } // Declare publisher printf("Declaring publisher for '%s'...\n", ctx->keyexpr_out); z_owned_publisher_t pub; z_publisher_options_t pub_opts; z_publisher_options_default(&pub_opts); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 pub_opts.allowed_destination = Z_LOCALITY_REMOTE; #endif if (z_declare_publisher(z_loan(s), &pub, z_loan(pub_qry_ke), &pub_opts) != Z_OK) { printf("Unable to declare publisher for key expression!\n"); return NULL; } // Declare querier printf("Declaring Querier on '%s'...\n", ctx->keyexpr_out); z_owned_querier_t querier; z_querier_options_t qry_opts; z_querier_options_default(&qry_opts); #if Z_FEATURE_LOCAL_QUERYABLE == 1 qry_opts.allowed_destination = Z_LOCALITY_REMOTE; #endif if (z_declare_querier(z_loan(s), &querier, z_loan(pub_qry_ke), &qry_opts) != Z_OK) { printf("Unable to declare Querier for key expression!\n"); return NULL; } // Wait for other nodes to come online z_sleep_s(5); // Send a join printf("Starting sending data\n"); // Publish data char buf[256]; for (int idx = 0; idx < tx_nb; ++idx) { z_sleep_s(1); // Create payload sprintf(buf, "[%4d] %s", idx, pub_val); printf("[Publisher %d] Sending ('%s': '%s')...\n", ctx->id, ctx->keyexpr_out, buf); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); // Send data z_publisher_put(z_loan(pub), z_move(payload), NULL); } // Querier data for (int idx = 0; idx < tx_nb; ++idx) { z_sleep_s(1); // Create payload sprintf(buf, "[%4d] %s", idx, ""); printf("[Querier %d] Sending ('%s': '%s')...\n", ctx->id, ctx->keyexpr_out, buf); // Query data z_owned_fifo_handler_reply_t qry_handler; z_owned_closure_reply_t qry_closure; z_fifo_channel_reply_new(&qry_closure, &qry_handler, 16); z_querier_get(z_loan(querier), NULL, z_move(qry_closure), NULL); // Process received data z_owned_reply_t reply; for (z_result_t res = z_recv(z_loan(qry_handler), &reply); res == Z_OK; res = z_recv(z_loan(qry_handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> [Query %d] Received ('%.*s': '%.*s')\n", ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } z_drop(z_move(reply)); } z_drop(z_move(qry_handler)); } // Wait for sub & queryable data z_sleep_s(5); assert(ctx->sub_msg_nb >= rx_nb); assert(ctx->qybl_msg_nb >= rx_nb); // Clean up z_drop(z_move(pub)); z_drop(z_move(querier)); z_drop(z_move(s)); return NULL; } static void test_packet_transmission(void) { // Create node context _node_ctx_t node_ctx_0 = {.keyexpr_out = "test/A", .id = 0, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_1 = {.keyexpr_out = "test/B", .id = 1, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_2 = {.keyexpr_out = "test/C", .id = 2, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_3 = {.keyexpr_out = "test/D", .id = 3, .qybl_msg_nb = 0, .sub_msg_nb = 0}; // Init config z_config_default(&node_ctx_0.config); z_config_default(&node_ctx_1.config); z_config_default(&node_ctx_2.config); z_config_default(&node_ctx_3.config); // Fill config zp_config_insert(z_loan_mut(node_ctx_0.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_0.config), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(node_ctx_1.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_1.config), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_LISTEN_KEY, "udp/224.0.0.224:7447#iface=lo"); // Init threads in a staggered manner to let time for sockets to establish _z_task_t task0, task1, task2, task3; _z_task_init(&task0, NULL, node_task, &node_ctx_0); z_sleep_ms(100); _z_task_init(&task1, NULL, node_task, &node_ctx_1); z_sleep_ms(100); _z_task_init(&task2, NULL, node_task, &node_ctx_2); z_sleep_ms(100); _z_task_init(&task3, NULL, node_task, &node_ctx_3); // Wait a bit z_sleep_s(20); // Clean up _z_task_join(&task0); _z_task_join(&task1); _z_task_join(&task2); _z_task_join(&task3); } int main(int argc, char **argv) { (void)argc; (void)argv; test_packet_transmission(); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf( "Missing config token to build this test. This test requires: Z_FEATURE_SUBSCRIPTION, Z_FEATURE_PUBLICATION, " "Z_FEATURE_QUERY, Z_FEATURE_QUERYABLE, Z_FEATURE_MULTI_THREAD and " "Z_FEATURE_UNSTABLE_API (until querier becomes stable)\n"); printf("It also requires Z_FEATURE_LOCAL_SUBSCRIBER to be deactivated\n"); return 0; } #endif ================================================ FILE: tests/z_test_peer_unicast.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_PUBLICATION == 1 && Z_FEATURE_QUERY == 1 && Z_FEATURE_QUERYABLE == 1 && \ Z_FEATURE_MULTI_THREAD == 1 && Z_FEATURE_LOCAL_SUBSCRIBER == 0 && Z_FEATURE_UNICAST_PEER == 1 && \ defined Z_FEATURE_UNSTABLE_API typedef struct _node_ctx { z_owned_config_t config; const char *keyexpr_out; int id; int sub_msg_nb; int qybl_msg_nb; } _node_ctx_t; const char *keyexpr_in = "test/**"; const char *qybl_val = "Queryable data"; const char *pub_val = "Publisher data"; const int tx_nb = 5; const int rx_nb = 15; void query_handler(z_loaned_query_t *query, void *ctx) { _node_ctx_t *node_ctx = (_node_ctx_t *)ctx; z_view_string_t keystr; z_keyexpr_as_view_string(z_query_keyexpr(query), &keystr); z_view_string_t params; z_query_parameters(query, ¶ms); printf(" >> [Queryable %d] Received ('%.*s': '%.*s')\n", node_ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(params)), z_string_data(z_loan(params))); // Reply value z_owned_bytes_t reply_payload; z_bytes_from_static_str(&reply_payload, qybl_val); z_query_reply(query, z_query_keyexpr(query), z_move(reply_payload), NULL); node_ctx->qybl_msg_nb++; } void pub_handler(z_loaned_sample_t *sample, void *ctx) { _node_ctx_t *node_ctx = (_node_ctx_t *)ctx; z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t value; z_bytes_to_string(z_sample_payload(sample), &value); printf(">> [Subscriber %d] Received ('%.*s': '%.*s')\n", node_ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(value)), z_string_data(z_loan(value))); z_drop(z_move(value)); node_ctx->sub_msg_nb++; } void *node_task(void *ptr) { _node_ctx_t *ctx = (_node_ctx_t *)ptr; // Open session printf("Opening session...\n"); z_owned_session_t s; if (z_open(&s, z_move(ctx->config), NULL) != Z_OK) { printf("Unable to open session!\n"); return NULL; } // Create keyexprs z_view_keyexpr_t sub_qybl_ke; if (z_view_keyexpr_from_str(&sub_qybl_ke, keyexpr_in) != Z_OK) { printf("%s is not a valid key expression\n", keyexpr_in); return NULL; } z_view_keyexpr_t pub_qry_ke; if (z_view_keyexpr_from_str(&pub_qry_ke, ctx->keyexpr_out) < 0) { printf("%s is not a valid key expression\n", ctx->keyexpr_out); return NULL; } // Declare subscriber printf("Declaring Subscriber on '%s'...\n", keyexpr_in); z_owned_closure_sample_t sub_cb; z_closure(&sub_cb, pub_handler, NULL, ctx); if (z_declare_background_subscriber(z_loan(s), z_loan(sub_qybl_ke), z_move(sub_cb), NULL) != Z_OK) { printf("Unable to declare subscriber.\n"); return NULL; } // Declare queryable printf("Creating Queryable on '%s'...\n", keyexpr_in); z_owned_closure_query_t qybl_cb; z_closure(&qybl_cb, query_handler, NULL, ctx); if (z_declare_background_queryable(z_loan(s), z_loan(sub_qybl_ke), z_move(qybl_cb), NULL) != Z_OK) { printf("Unable to create queryable.\n"); return NULL; } // Declare publisher printf("Declaring publisher for '%s'...\n", ctx->keyexpr_out); z_owned_publisher_t pub; z_publisher_options_t pub_opts; z_publisher_options_default(&pub_opts); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 pub_opts.allowed_destination = Z_LOCALITY_REMOTE; #endif if (z_declare_publisher(z_loan(s), &pub, z_loan(pub_qry_ke), &pub_opts) != Z_OK) { printf("Unable to declare publisher for key expression!\n"); return NULL; } // Declare querier printf("Declaring Querier on '%s'...\n", ctx->keyexpr_out); z_owned_querier_t querier; z_querier_options_t qry_opts; z_querier_options_default(&qry_opts); #if Z_FEATURE_LOCAL_QUERYABLE == 1 qry_opts.allowed_destination = Z_LOCALITY_REMOTE; #endif if (z_declare_querier(z_loan(s), &querier, z_loan(pub_qry_ke), &qry_opts) != Z_OK) { printf("Unable to declare Querier for key expression!\n"); return NULL; } // Wait for other nodes to come online z_sleep_s(5); printf("Starting sending data\n"); // Publish data char buf[256]; for (int idx = 0; idx < tx_nb; ++idx) { z_sleep_s(1); // Create payload sprintf(buf, "[%4d] %s", idx, pub_val); printf("[Publisher %d] Sending ('%s': '%s')...\n", ctx->id, ctx->keyexpr_out, buf); z_owned_bytes_t payload; z_bytes_copy_from_str(&payload, buf); // Send data z_publisher_put(z_loan(pub), z_move(payload), NULL); } // Querier data for (int idx = 0; idx < tx_nb; ++idx) { z_sleep_s(1); // Create payload sprintf(buf, "[%4d] %s", idx, ""); printf("[Querier %d] Sending ('%s': '%s')...\n", ctx->id, ctx->keyexpr_out, buf); // Query data z_owned_fifo_handler_reply_t qry_handler; z_owned_closure_reply_t qry_closure; z_fifo_channel_reply_new(&qry_closure, &qry_handler, 16); z_querier_get(z_loan(querier), NULL, z_move(qry_closure), NULL); // Process received data z_owned_reply_t reply; for (z_result_t res = z_recv(z_loan(qry_handler), &reply); res == Z_OK; res = z_recv(z_loan(qry_handler), &reply)) { if (z_reply_is_ok(z_loan(reply))) { const z_loaned_sample_t *sample = z_reply_ok(z_loan(reply)); z_view_string_t keystr; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &keystr); z_owned_string_t replystr; z_bytes_to_string(z_sample_payload(sample), &replystr); printf(">> [Query %d] Received ('%.*s': '%.*s')\n", ctx->id, (int)z_string_len(z_loan(keystr)), z_string_data(z_loan(keystr)), (int)z_string_len(z_loan(replystr)), z_string_data(z_loan(replystr))); z_drop(z_move(replystr)); } z_drop(z_move(reply)); } z_drop(z_move(qry_handler)); } // Wait for sub & queryable data z_sleep_s(5); printf("Node %d: Received %d subs, %d queries\n", ctx->id, ctx->sub_msg_nb, ctx->qybl_msg_nb); assert(ctx->sub_msg_nb >= rx_nb); assert(ctx->qybl_msg_nb >= rx_nb); // Clean up z_drop(z_move(pub)); z_drop(z_move(querier)); z_drop(z_move(s)); return NULL; } static void test_packet_transmission(void) { // Create node context _node_ctx_t node_ctx_0 = {.keyexpr_out = "test/A", .id = 0, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_1 = {.keyexpr_out = "test/B", .id = 1, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_2 = {.keyexpr_out = "test/C", .id = 2, .qybl_msg_nb = 0, .sub_msg_nb = 0}; _node_ctx_t node_ctx_3 = {.keyexpr_out = "test/D", .id = 3, .qybl_msg_nb = 0, .sub_msg_nb = 0}; // Init config z_config_default(&node_ctx_0.config); z_config_default(&node_ctx_1.config); z_config_default(&node_ctx_2.config); z_config_default(&node_ctx_3.config); // Fill config zp_config_insert(z_loan_mut(node_ctx_0.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_0.config), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:7447"); zp_config_insert(z_loan_mut(node_ctx_1.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_1.config), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:7448"); zp_config_insert(z_loan_mut(node_ctx_1.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:7449"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); zp_config_insert(z_loan_mut(node_ctx_2.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7448"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7448"); zp_config_insert(z_loan_mut(node_ctx_3.config), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7449"); // Init threads in a staggered manner to let time for sockets to establish _z_task_t task0, task1, task2, task3; _z_task_init(&task0, NULL, node_task, &node_ctx_0); z_sleep_ms(100); _z_task_init(&task1, NULL, node_task, &node_ctx_1); z_sleep_ms(100); _z_task_init(&task2, NULL, node_task, &node_ctx_2); z_sleep_ms(100); _z_task_init(&task3, NULL, node_task, &node_ctx_3); // Wait a bit z_sleep_s(30); // Clean up _z_task_join(&task0); _z_task_join(&task1); _z_task_join(&task2); _z_task_join(&task3); } static bool test_peer_connection(void) { // Init config z_owned_config_t config; z_config_default(&config); zp_config_insert(z_loan_mut(config), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(config), Z_CONFIG_LISTEN_KEY, "tcp/127.0.0.1:7447"); // Open main session z_owned_session_t s; if (z_open(&s, z_move(config), NULL) != Z_OK) { printf("Unable to open main session!\n"); return false; } z_owned_session_t sess_array[Z_LISTEN_MAX_CONNECTION_NB + 1]; z_owned_config_t cfg_array[Z_LISTEN_MAX_CONNECTION_NB + 1]; // // Open max peers for (int i = 0; i < Z_LISTEN_MAX_CONNECTION_NB; i++) { z_config_default(&cfg_array[i]); zp_config_insert(z_loan_mut(cfg_array[i]), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(cfg_array[i]), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); if (z_open(&sess_array[i], z_move(cfg_array[i]), NULL) != Z_OK) { printf("Unable to open peer session!\n"); return false; } z_sleep_ms(100); } // Fail to open a new one z_config_default(&cfg_array[Z_LISTEN_MAX_CONNECTION_NB]); zp_config_insert(z_loan_mut(cfg_array[Z_LISTEN_MAX_CONNECTION_NB]), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(cfg_array[Z_LISTEN_MAX_CONNECTION_NB]), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); if (z_open(&sess_array[Z_LISTEN_MAX_CONNECTION_NB], z_move(cfg_array[Z_LISTEN_MAX_CONNECTION_NB]), NULL) == Z_OK) { printf("Should not have been able to open this session\n"); return false; } // Close first session z_drop(z_move(sess_array[0])); z_sleep_ms(100); // Alternate opening and closing first session a few times for (int i = 0; i < 5; i++) { z_config_default(&cfg_array[0]); zp_config_insert(z_loan_mut(cfg_array[0]), Z_CONFIG_MODE_KEY, "peer"); zp_config_insert(z_loan_mut(cfg_array[0]), Z_CONFIG_CONNECT_KEY, "tcp/127.0.0.1:7447"); if (z_open(&sess_array[0], z_move(cfg_array[0]), NULL) != Z_OK) { printf("Unable to open peer session!\n"); return false; } z_sleep_ms(100); z_drop(z_move(sess_array[0])); z_sleep_ms(100); } for (size_t i = 0; i < _ZP_ARRAY_SIZE(sess_array); i++) { z_drop(z_move(sess_array[i])); } z_drop(z_move(s)); return true; } int main(int argc, char **argv) { (void)argc; (void)argv; test_packet_transmission(); printf("Test connections..."); if (!test_peer_connection()) { return -1; } printf(" Ok\n"); return 0; } #else int main(int argc, char **argv) { (void)argc; (void)argv; printf( "Missing config token to build this test. This test requires: Z_FEATURE_SUBSCRIPTION, Z_FEATURE_PUBLICATION, " "Z_FEATURE_QUERY, Z_FEATURE_QUERYABLE, Z_FEATURE_MULTI_THREAD, Z_FEATURE_UNICAST_PEER and " "Z_FEATURE_UNSTABLE_API (until querier becomes stable)\n"); printf("It also requires Z_FEATURE_LOCAL_SUBSCRIBER to be deactivated\n"); return 0; } #endif ================================================ FILE: tests/z_tls_config_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include #include "zenoh-pico.h" #if Z_FEATURE_LINK_TLS == 1 #include "mbedtls/base64.h" static const char SERVER_CA_PEM[] = "-----BEGIN CERTIFICATE-----\n" "MIIDSzCCAjOgAwIBAgIITcwv1N10nqEwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE\n" "AxMVbWluaWNhIHJvb3QgY2EgNGRjYzJmMCAXDTIzMDMwNjE2NDEwNloYDzIxMjMw\n" "MzA2MTY0MTA2WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA0ZGNjMmYwggEi\n" "MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2WUgN7NMlXIknew1cXiTWGmS0\n" "1T1EjcNNDAq7DqZ7/ZVXrjD47yxTt5EOiOXK/cINKNw4Zq/MKQvq9qu+Oax4lwiV\n" "Ha0i8ShGLSuYI1HBlXu4MmvdG+3/SjwYoGsGaShr0y/QGzD3cD+DQZg/RaaIPHlO\n" "MdmiUXxkMcy4qa0hFJ1imlJdq/6Tlx46X+0vRCh8nkekvOZR+t7Z5U4jn4XE54Kl\n" "0PiwcyX8vfDZ3epa/FSHZvVQieM/g5Yh9OjIKCkdWRg7tD0IEGsaW11tEPJ5SiQr\n" "mDqdRneMzZKqY0xC+QqXSvIlzpOjiu8PYQx7xugaUFE/npKRQdvh8ojHJMdNAgMB\n" "AAGjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr\n" "BgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTX46+p+Po1npE6\n" "QLQ7mMI+83s6qDAfBgNVHSMEGDAWgBTX46+p+Po1npE6QLQ7mMI+83s6qDANBgkq\n" "hkiG9w0BAQsFAAOCAQEAaN0IvEC677PL/JXzMrXcyBV88IvimlYN0zCt48GYlhmx\n" "vL1YUDFLJEB7J+dyERGE5N6BKKDGblC4WiTFgDMLcHFsMGRc0v7zKPF1PSBwRYJi\n" "ubAmkwdunGG5pDPUYtTEDPXMlgClZ0YyqSFJMOqA4IzQg6exVjXtUxPqzxNhyC7S\n" "vlgUwPbX46uNi581a9+Ls2V3fg0ZnhkTSctYZHGZNeh0Nsf7Am8xdUDYG/bZcVef\n" "jbQ9gpChosdjF0Bgblo7HSUct/2Va+YlYwW+WFjJX8k4oN6ZU5W5xhdfO8Czmgwk\n" "US5kJ/+1M0uR8zUhZHL61FbsdPxEj+fYKrHv4woo+A==\n" "-----END CERTIFICATE-----\n"; static const char SERVER_CERT_PEM[] = "-----BEGIN CERTIFICATE-----\n" "MIIDLjCCAhagAwIBAgIIW1mAtJWJAJYwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE\n" "AxMVbWluaWNhIHJvb3QgY2EgNGRjYzJmMCAXDTIzMDMwNjE2NDEwNloYDzIxMjMw\n" "MzA2MTY0MTA2WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB\n" "AQUAA4IBDwAwggEKAoIBAQCYMLJKooc+YRlKEMfeV09pX9myH34eUcUuT0fXS8lm\n" "PlZ/NW7mm5lDwa8EUg61WuXQv2ouQDptmIcdeb/w4RW93Xflkyng1Xbd91OwQBJd\n" "+8ZVBjzL7hSRk3QPDqx/CVBU/I1GmXKzb6cWzq1fTkOn1WLNXf21I6p7+N3qHLPF\n" "JQeoVq1HBBFcAjTgJnpyQNvRGLDuLTK+OsWEGib2U8qrgiRdkaBLkxGXSlGABlOo\n" "cQyW/zOhf4pwb2Z/JAge2mRW5IcexCPBWint8ydPsoJDds8j5+AyYCD6HUhHX0Ob\n" "Qkz73OW7f2PQhuTK2uzKy0Yz6lNFt2nuzaWC04wIW3T7AgMBAAGjdjB0MA4GA1Ud\n" "DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T\n" "AQH/BAIwADAfBgNVHSMEGDAWgBTX46+p+Po1npE6QLQ7mMI+83s6qDAUBgNVHREE\n" "DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAAxrmQPG54ybKgMVliN8\n" "Mg5povSdPIVVnlU/HOVG9yxzAOav/xQP003M4wqpatWxI8tR1PcLuZf0EPmcdJgb\n" "tVl9nZMVZtveQnYMlU8PpkEVu56VM4Zr3rH9liPRlr0JEAXODdKw76kWKzmdqWZ/\n" "rzhup3Ek7iEX6T5j/cPUvTWtMD4VEK2I7fgoKSHIX8MIVzqM7cuboGWPtS3eRNXl\n" "MgvahA4TwLEXPEe+V1WAq6nSb4g2qSXWIDpIsy/O1WGS/zzRnKvXu9/9NkXWqZMl\n" "C1LSpiiQUaRSglOvYf/Zx6r+4BOS4OaaArwHkecZQqBSCcBLEAyb/FaaXdBowI0U\n" "PQ4=\n" "-----END CERTIFICATE-----\n"; static const char SERVER_KEY_PEM[] = "-----BEGIN RSA PRIVATE KEY-----\n" "MIIEpAIBAAKCAQEAmDCySqKHPmEZShDH3ldPaV/Zsh9+HlHFLk9H10vJZj5WfzVu\n" "5puZQ8GvBFIOtVrl0L9qLkA6bZiHHXm/8OEVvd135ZMp4NV23fdTsEASXfvGVQY8\n" "y+4UkZN0Dw6sfwlQVPyNRplys2+nFs6tX05Dp9VizV39tSOqe/jd6hyzxSUHqFat\n" "RwQRXAI04CZ6ckDb0Riw7i0yvjrFhBom9lPKq4IkXZGgS5MRl0pRgAZTqHEMlv8z\n" "oX+KcG9mfyQIHtpkVuSHHsQjwVop7fMnT7KCQ3bPI+fgMmAg+h1IR19Dm0JM+9zl\n" "u39j0IbkytrsystGM+pTRbdp7s2lgtOMCFt0+wIDAQABAoIBADNTSO2uvlmlOXgn\n" "DKDJZTiuYKaXxFrJTOx/REUxg+x9XYJtLMeM9jVJnpKgceFrlFHAHDkY5BuN8xNX\n" "ugmsfz6W8BZ2eQsgMoRNIuYv1YHopUyLW/mSg1FNHzjsw/Pb2kGvIp4Kpgopv3oL\n" "naCkrmBtsHJ+Hk/2hUpl9cE8iMwVWcVevLzyHi98jNy1IDdIPhRtl0dhMiqC5MRr\n" "4gLJ5gNkLYX7xf3tw5Hmfk/bVNProqZXDIQVI7rFvItX586nvQ3LNQkmW/D2ShZf\n" "3FEqMu6EdA2Ycc4UZgAlQNGV0VBrWWVXizOQ+9gjLnBk3kJjqfigCU6NG94bTJ+H\n" "0YIhsGECgYEAwdSSyuMSOXgzZQ7Vv+GsNn/7ivi/H8eb/lDzksqS/JroA2ciAmHG\n" "2OF30eUJKRg+STqBTpOfXgS4QUa8QLSwBSnwcw6579x9bYGUhqD2Ypaw9uCnOukA\n" "CwwggZ9cDmF0tb5rYjqkW3bFPqkCnTGb0ylMFaYRhRDU20iG5t8PQckCgYEAyQEM\n" "KK18FLQUKivGrQgP5Ib6IC3myzlHGxDzfobXGpaQntFnHY7Cxp/6BBtmASzt9Jxu\n" "etnrevmzrbKqsLTJSg3ivbiq0YTLAJ1FsZrCp71dx49YR/5o9QFiq0nQoKnwUVeb\n" "/hrDjMAokNkjFL5vouXO711GSS6YyM4WzAKZAqMCgYEAhqGxaG06jmJ4SFx6ibIl\n" "nSFeRhQrJNbP+mCeHrrIR98NArgS/laN+Lz7LfaJW1r0gIa7pCmTi4l5thV80vDu\n" "RlfwJOr4qaucD4Du+mg5WxdSSdiXL6sBlarRtVdMaMy2dTqTegJDgShJLxHTt/3q\n" "P0yzBWJ5TtT3FG0XDqum/EkCgYAYNHwWWe3bQGQ9P9BI/fOL/YUZYu2sA1XAuKXZ\n" "0rsMhJ0dwvG76XkjGhitbe82rQZqsnvLZ3qn8HHmtOFBLkQfGtT3K8nGOUuI42eF\n" "H7HZKUCly2lCIizZdDVBkz4AWvaJlRc/3lE2Hd3Es6E52kTvROVKhdz06xuS8t5j\n" "6twqKQKBgQC01AeiWL6Rzo+yZNzVgbpeeDogaZz5dtmURDgCYH8yFX5eoCKLHfnI\n" "2nDIoqpaHY0LuX+dinuH+jP4tlyndbc2muXnHd9r0atytxA69ay3sSA5WFtfi4ef\n" "ESElGO6qXEA821RpQp+2+uhL90+iC294cPqlS5LDmvTMypVDHzrxPQ==\n" "-----END RSA PRIVATE KEY-----\n"; static char *encode_base64_strdup(const char *input) { if (input == NULL) { return NULL; } size_t input_len = strnlen(input, 64 * 1024); size_t output_len = 0; int rc = mbedtls_base64_encode(NULL, 0, &output_len, (const unsigned char *)input, input_len); assert(rc == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL); unsigned char *buffer = (unsigned char *)malloc(output_len + 1); assert(buffer != NULL); rc = mbedtls_base64_encode(buffer, output_len, &output_len, (const unsigned char *)input, input_len); if (rc != 0) { fprintf(stderr, "mbedtls_base64_encode(data) failed: %d\n", rc); free(buffer); return NULL; } buffer[output_len] = '\0'; return (char *)buffer; } static volatile bool g_received = false; static void tls_sample_handler(z_loaned_sample_t *sample, void *ctx) { const char *expected = (const char *)ctx; // flawfinder: ignore[CWE-126] size_t expected_len = expected ? strlen(expected) : 0; z_owned_string_t payload; if (z_bytes_to_string(z_sample_payload(sample), &payload) != Z_OK) { fprintf(stderr, "subscriber: failed to decode payload\n"); return; } size_t payload_len = z_string_len(z_loan(payload)); if (expected_len != payload_len || memcmp(expected, z_string_data(z_loan(payload)), expected_len) != 0) { fprintf(stderr, "subscriber: unexpected payload\n"); z_drop(z_move(payload)); return; } g_received = true; z_drop(z_move(payload)); } int main(void) { char locator_buf[64]; snprintf(locator_buf, sizeof(locator_buf), "tls/127.0.0.1:7447"); const char *locator = locator_buf; static const char *const keyexpr_str = "test/tls/config"; static const char *const payload_str = "tls-config-ok"; char *ca_base64 = encode_base64_strdup(SERVER_CA_PEM); char *cert_base64 = encode_base64_strdup(SERVER_CERT_PEM); char *key_base64 = encode_base64_strdup(SERVER_KEY_PEM); if (ca_base64 == NULL || cert_base64 == NULL || key_base64 == NULL) { fprintf(stderr, "failed to prepare TLS credentials\n"); goto cleanup_buffers; } z_owned_config_t listen_cfg; z_config_default(&listen_cfg); zp_config_insert(z_loan_mut(listen_cfg), Z_CONFIG_MODE_KEY, Z_CONFIG_MODE_PEER); zp_config_insert(z_loan_mut(listen_cfg), Z_CONFIG_LISTEN_KEY, locator); zp_config_insert(z_loan_mut(listen_cfg), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY, ca_base64); zp_config_insert(z_loan_mut(listen_cfg), Z_CONFIG_TLS_LISTEN_CERTIFICATE_BASE64_KEY, cert_base64); zp_config_insert(z_loan_mut(listen_cfg), Z_CONFIG_TLS_LISTEN_PRIVATE_KEY_BASE64_KEY, key_base64); z_owned_session_t server; z_result_t res = z_open(&server, z_move(listen_cfg), NULL); if (res != Z_OK) { fprintf(stderr, "server z_open failed: %d\n", res); return 1; } (void)z_sleep_ms(50); z_view_keyexpr_t keyexpr; if (z_view_keyexpr_from_str(&keyexpr, keyexpr_str) != Z_OK) { fprintf(stderr, "failed to create keyexpr view\n"); goto cleanup_server; } z_owned_closure_sample_t callback; z_closure(&callback, tls_sample_handler, NULL, (void *)payload_str); z_owned_subscriber_t subscriber; z_internal_null(&subscriber); z_subscriber_options_t sub_opts; z_subscriber_options_default(&sub_opts); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 sub_opts.allowed_origin = Z_LOCALITY_ANY; #endif if (z_declare_subscriber(z_loan(server), &subscriber, z_loan(keyexpr), z_move(callback), &sub_opts) != Z_OK) { fprintf(stderr, "server: failed to declare subscriber\n"); goto cleanup_server; } z_owned_config_t connect_cfg; z_config_default(&connect_cfg); zp_config_insert(z_loan_mut(connect_cfg), Z_CONFIG_CONNECT_KEY, locator); zp_config_insert(z_loan_mut(connect_cfg), Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY, ca_base64); zp_config_insert(z_loan_mut(connect_cfg), Z_CONFIG_TLS_VERIFY_NAME_ON_CONNECT_KEY, "false"); z_owned_session_t client; res = z_open(&client, z_move(connect_cfg), NULL); if (res != Z_OK) { fprintf(stderr, "client z_open failed: %d\n", res); return 1; } (void)z_sleep_ms(50); z_owned_publisher_t publisher; z_internal_null(&publisher); z_publisher_options_t pub_opts; z_publisher_options_default(&pub_opts); #if Z_FEATURE_LOCAL_SUBSCRIBER == 1 pub_opts.allowed_destination = Z_LOCALITY_ANY; #endif if (z_declare_publisher(z_loan(client), &publisher, z_loan(keyexpr), &pub_opts) != Z_OK) { fprintf(stderr, "client: failed to declare publisher\n"); goto cleanup_client; } z_owned_bytes_t payload; z_internal_null(&payload); if (z_bytes_copy_from_str(&payload, payload_str) != Z_OK) { fprintf(stderr, "client: failed to build payload\n"); goto cleanup_pub; } if (z_publisher_put(z_loan(publisher), z_move(payload), NULL) != Z_OK) { fprintf(stderr, "client: publisher put failed\n"); goto cleanup_pub; } for (int i = 0; (i < 200) && !g_received; ++i) { z_sleep_ms(10); } if (!g_received) { fprintf(stderr, "subscriber: did not receive payload\n"); } cleanup_pub: if (z_internal_check(publisher)) { z_drop(z_move(publisher)); } cleanup_client: if (z_internal_check(subscriber)) { z_drop(z_move(subscriber)); } cleanup_server: z_session_drop(z_session_move(&client)); z_session_drop(z_session_move(&server)); cleanup_buffers: if (key_base64 != NULL) { free(key_base64); } if (cert_base64 != NULL) { free(cert_base64); } if (ca_base64 != NULL) { free(ca_base64); } return g_received ? 0 : 1; } #else int main(void) { printf("TLS feature not enabled, skipping test\n"); return 0; } #endif ================================================ FILE: tests/z_tls_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include "zenoh-pico/collections/string.h" #include "zenoh-pico/link/config/tls.h" #include "zenoh-pico/utils/config.h" #include "zenoh-pico/utils/result.h" #undef NDEBUG #include int main(void) { #if Z_FEATURE_LINK_TLS == 1 printf(">>> Testing TLS config parsing...\n"); _z_str_intmap_t config; _z_str_intmap_init(&config); z_result_t res = _z_tls_config_from_str(&config, "root_ca_certificate=/etc/ssl/ca.pem"); assert(res == _Z_RES_OK); assert(_z_str_intmap_len(&config) == 1); char *ca_cert = _z_str_intmap_get(&config, TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY); assert(ca_cert != NULL); assert(_z_str_eq(ca_cert, "/etc/ssl/ca.pem") == true); _z_str_intmap_clear(&config); _z_str_intmap_init(&config); res = _z_tls_config_from_str(&config, "root_ca_certificate=ca.pem;enable_mtls=1;connect_private_key=client.key;" "connect_certificate=client.pem;verify_name_on_connect=0;" "root_ca_certificate_base64=BASE64_CA;connect_private_key_base64=BASE64_KEY;" "connect_certificate_base64=BASE64_CERT"); assert(res == _Z_RES_OK); assert(_z_str_intmap_len(&config) == 8); ca_cert = _z_str_intmap_get(&config, TLS_CONFIG_ROOT_CA_CERTIFICATE_KEY); assert(ca_cert != NULL); assert(_z_str_eq(ca_cert, "ca.pem") == true); char *enable = _z_str_intmap_get(&config, TLS_CONFIG_ENABLE_MTLS_KEY); assert(enable != NULL); assert(_z_str_eq(enable, "1") == true); char *client_key = _z_str_intmap_get(&config, TLS_CONFIG_CONNECT_PRIVATE_KEY_KEY); assert(client_key != NULL); assert(_z_str_eq(client_key, "client.key") == true); char *client_cert = _z_str_intmap_get(&config, TLS_CONFIG_CONNECT_CERTIFICATE_KEY); assert(client_cert != NULL); assert(_z_str_eq(client_cert, "client.pem") == true); char *verify = _z_str_intmap_get(&config, TLS_CONFIG_VERIFY_NAME_ON_CONNECT_KEY); assert(verify != NULL); assert(_z_str_eq(verify, "0") == true); char *ca_base64 = _z_str_intmap_get(&config, TLS_CONFIG_ROOT_CA_CERTIFICATE_BASE64_KEY); assert(ca_base64 != NULL && _z_str_eq(ca_base64, "BASE64_CA") == true); char *client_key_inline = _z_str_intmap_get(&config, TLS_CONFIG_CONNECT_PRIVATE_KEY_BASE64_KEY); assert(client_key_inline != NULL && _z_str_eq(client_key_inline, "BASE64_KEY") == true); _z_str_intmap_clear(&config); _z_str_intmap_init(&config); res = _z_tls_config_from_str(&config, ""); assert(res == _Z_RES_OK); assert(_z_str_intmap_is_empty(&config) == true); _z_str_intmap_clear(&config); _z_config_t session_cfg; _z_config_init(&session_cfg); assert(_zp_config_insert(&session_cfg, Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY, "/session/ca.pem") == _Z_RES_OK); assert(_zp_config_insert(&session_cfg, Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_BASE64_KEY, "SESSION_CA") == _Z_RES_OK); assert(_zp_config_insert(&session_cfg, Z_CONFIG_TLS_ENABLE_MTLS_KEY, "true") == _Z_RES_OK); assert(_zp_config_insert(&session_cfg, Z_CONFIG_TLS_CONNECT_CERTIFICATE_BASE64_KEY, "SESSION_CERT") == _Z_RES_OK); assert(_z_str_eq(_z_config_get(&session_cfg, Z_CONFIG_TLS_ROOT_CA_CERTIFICATE_KEY), "/session/ca.pem")); assert(_z_str_eq(_z_config_get(&session_cfg, Z_CONFIG_TLS_ENABLE_MTLS_KEY), "true")); _z_config_clear(&session_cfg); #else printf("TLS feature not enabled, skipping tests\n"); #endif return 0; } ================================================ FILE: tests/z_utils_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include "zenoh-pico/utils/pointers.h" #include "zenoh-pico/utils/query_params.h" #include "zenoh-pico/utils/time_range.h" #undef NDEBUG #include static void test_query_params(void) { #define TEST_PARAMS(str, expected, n) \ { \ _z_str_se_t params = _z_bstrnew(str); \ \ for (int i = 0; i <= n; i++) { \ _z_query_param_t param = _z_query_params_next(¶ms); \ if (i < n) { \ assert(param.key.start == expected[i].key.start); \ assert(param.key.end == expected[i].key.end); \ assert(param.value.start == expected[i].value.start); \ assert(param.value.end == expected[i].value.end); \ } else { \ assert(param.key.start == NULL); \ assert(param.key.end == NULL); \ assert(param.value.start == NULL); \ assert(param.value.end == NULL); \ } \ } \ assert(params.start == NULL); \ assert(params.end == NULL); \ } const char *params1 = ""; const _z_query_param_t *params1_expected = NULL; TEST_PARAMS(params1, params1_expected, 0); const char *params2 = "a=1"; const _z_query_param_t params2_expected[] = {{ .key.start = params2, .key.end = _z_cptr_char_offset(params2, 1), .value.start = _z_cptr_char_offset(params2, 2), .value.end = _z_cptr_char_offset(params2, 3), }}; TEST_PARAMS(params2, params2_expected, 1); const char *params3 = "a=1;bee=string"; const _z_query_param_t params3_expected[] = {{ .key.start = params3, .key.end = _z_cptr_char_offset(params3, 1), .value.start = _z_cptr_char_offset(params3, 2), .value.end = _z_cptr_char_offset(params3, 3), }, { .key.start = _z_cptr_char_offset(params3, 4), .key.end = _z_cptr_char_offset(params3, 7), .value.start = _z_cptr_char_offset(params3, 8), .value.end = _z_cptr_char_offset(params3, 14), }}; TEST_PARAMS(params3, params3_expected, 2); const char *params4 = ";"; const _z_query_param_t params4_expected[] = {{ .key.start = NULL, .key.end = NULL, .value.start = NULL, .value.end = NULL, }}; TEST_PARAMS(params4, params4_expected, 1); const char *params5 = "a"; const _z_query_param_t params5_expected[] = {{ .key.start = params5, .key.end = _z_cptr_char_offset(params5, 1), .value.start = NULL, .value.end = NULL, }}; TEST_PARAMS(params5, params5_expected, 1); const char *params6 = "a="; const _z_query_param_t params6_expected[] = {{ .key.start = params6, .key.end = _z_cptr_char_offset(params6, 1), .value.start = NULL, .value.end = NULL, }}; TEST_PARAMS(params6, params6_expected, 1); } static bool compare_double_result(const double expected, const double result) { static const double EPSILON = 1e-6; return fabs(result - expected) < EPSILON; } static bool compare_time_range(const _z_time_range_t *a, const _z_time_range_t *b) { return a->start.bound == b->start.bound && compare_double_result(a->start.now_offset, b->start.now_offset) && a->end.bound == b->end.bound && compare_double_result(a->end.now_offset, b->end.now_offset); } static void test_time_range_roundtrip(const char *input) { _z_time_range_t parsed1 = {0}, parsed2 = {0}; char buf[128]; // SAFETY: input should be a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(input, strlen(input), &parsed1)); assert(_z_time_range_to_str(&parsed1, buf, sizeof(buf))); // SAFETY: _z_time_range_to_str() creates a null-terminated string if successful. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(buf, strlen(buf), &parsed2)); printf("Round-trip: input='%s' → output='%s'\n", input, buf); assert(compare_time_range(&parsed1, &parsed2)); } static void test_time_range(void) { _z_time_range_t result; const char *range = ""; // Range tests range = "[..]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_UNBOUNDED); assert(result.end.bound == _Z_TIME_BOUND_UNBOUNDED); test_time_range_roundtrip(range); range = "[now()..now(5)]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(5.0, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now(-999.9u)..now(100.5ms)]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(-0.0009999, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.1005, result.end.now_offset)); test_time_range_roundtrip(range); range = "]now(-87.6s)..now(1.5m)["; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_EXCLUSIVE); assert(compare_double_result(-87.6, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_EXCLUSIVE); assert(compare_double_result(90.0, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now(-24.5h)..now(6.75d)]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(-88200.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(583200.0, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now(-1.75w)..now()]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(-1058400.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.end.now_offset)); test_time_range_roundtrip(range); // Duration tests range = "[now();7.3]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(7.3, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();97.4u]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0000974, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();568.4ms]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.5684, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();9.4s]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(9.4, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();6.89m]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(413.4, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();1.567h]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(5641.2, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();2.7894d]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(241004.16, result.end.now_offset)); test_time_range_roundtrip(range); range = "[now();5.9457w]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == true); assert(result.start.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(0.0, result.start.now_offset)); assert(result.end.bound == _Z_TIME_BOUND_INCLUSIVE); assert(compare_double_result(3595959.36, result.end.now_offset)); test_time_range_roundtrip(range); // Error cases range = ""; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); range = "[;]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); range = "[now();]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); range = "[now()..5.6]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); range = "[now();s]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); range = "[now();one]"; // SAFETY: range is a null-terminated string. // Flawfinder: ignore [CWE-126] assert(_z_time_range_from_str(range, strlen(range), &result) == false); } #define STUB_SECS 1000u /* 16-Aug-1970 02:46:40 UTC in Unix time */ #define STUB_NANOS 0u static void test_time_range_contains_null_pointer(void) { _z_ntp64_t t = _z_timestamp_ntp64_from_time(STUB_SECS, STUB_NANOS); assert(_z_time_range_contains_at_time(NULL, 12345u, t) == false); } static void test_time_range_contains_unbounded_range(void) { _z_time_range_t r = {.start = {.bound = _Z_TIME_BOUND_UNBOUNDED, .now_offset = 0}, .end = {.bound = _Z_TIME_BOUND_UNBOUNDED, .now_offset = 0}}; _z_ntp64_t now = _z_timestamp_ntp64_from_time(STUB_SECS, STUB_NANOS); _z_ntp64_t before = now - 42; _z_ntp64_t after = now + 42; assert(_z_time_range_contains_at_time(&r, before, now) == true); assert(_z_time_range_contains_at_time(&r, now, now) == true); assert(_z_time_range_contains_at_time(&r, after, now) == true); } static void test_time_range_contains_lower_bound_inclusive_exclusive(void) { _z_time_range_t r = {.start = {.now_offset = 0}, .end = {.bound = _Z_TIME_BOUND_UNBOUNDED, .now_offset = 0}}; _z_ntp64_t now = _z_timestamp_ntp64_from_time(STUB_SECS, STUB_NANOS); _z_ntp64_t before = now - 1; _z_ntp64_t after = now + 1; // Inclusive [now .. +∞) r.start.bound = _Z_TIME_BOUND_INCLUSIVE; assert(_z_time_range_contains_at_time(&r, now, now) == true); assert(_z_time_range_contains_at_time(&r, before, now) == false); assert(_z_time_range_contains_at_time(&r, after, now) == true); // Exclusive ]now .. +∞) r.start.bound = _Z_TIME_BOUND_EXCLUSIVE; assert(_z_time_range_contains_at_time(&r, now, now) == false); assert(_z_time_range_contains_at_time(&r, before, now) == false); assert(_z_time_range_contains_at_time(&r, after, now) == true); } static void test_time_range_contains_upper_bound_inclusive_exclusive(void) { _z_time_range_t r = {.start = {.bound = _Z_TIME_BOUND_UNBOUNDED, .now_offset = 0}, .end = {.now_offset = 0}}; _z_ntp64_t now = _z_timestamp_ntp64_from_time(STUB_SECS, STUB_NANOS); _z_ntp64_t before = now - 1; _z_ntp64_t after = now + 1; // Inclusive (-∞ .. now] r.end.bound = _Z_TIME_BOUND_INCLUSIVE; assert(_z_time_range_contains_at_time(&r, before, now) == true); assert(_z_time_range_contains_at_time(&r, now, now) == true); assert(_z_time_range_contains_at_time(&r, after, now) == false); // Exclusive (-∞ .. now[ r.end.bound = _Z_TIME_BOUND_EXCLUSIVE; assert(_z_time_range_contains_at_time(&r, before, now) == true); assert(_z_time_range_contains_at_time(&r, now, now) == false); assert(_z_time_range_contains_at_time(&r, after, now) == false); } static void test_time_range_contains_fully_bounded_mixed(void) { // [now-5 .. now+5[ (inclusive start, exclusive end) _z_time_range_t r = {.start = {.bound = _Z_TIME_BOUND_INCLUSIVE, .now_offset = -5}, .end = {.bound = _Z_TIME_BOUND_EXCLUSIVE, .now_offset = 5}}; _z_ntp64_t now = _z_timestamp_ntp64_from_time(STUB_SECS, STUB_NANOS); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, -6.0), now) == false); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, -5.0), now) == true); // Inclusive assert(_z_time_range_contains_at_time(&r, now, now) == true); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 4.0), now) == true); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 5.0), now) == false); // Exclusive assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 6.0), now) == false); // Swap bound types r.start.bound = _Z_TIME_BOUND_EXCLUSIVE; r.end.bound = _Z_TIME_BOUND_INCLUSIVE; assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, -6.0), now) == false); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, -5.0), now) == false); // Exclusive assert(_z_time_range_contains_at_time(&r, now, now) == true); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 4.0), now) == true); assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 5.0), now) == true); // Inclusive assert(_z_time_range_contains_at_time(&r, _z_time_range_resolve_offset(now, 6.0), now) == false); } int main(void) { test_query_params(); test_time_range(); test_time_range_contains_null_pointer(); test_time_range_contains_unbounded_range(); test_time_range_contains_lower_bound_inclusive_exclusive(); test_time_range_contains_upper_bound_inclusive_exclusive(); test_time_range_contains_fully_bounded_mixed(); return 0; } ================================================ FILE: tests/z_wildcard_subscription_test.c ================================================ // // Copyright (c) 2025 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // // Copyright (c) 2022 ZettaScale Technology // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // #include #include #include #include #include #include #include "zenoh-pico.h" #include "zenoh-pico/api/types.h" #include "zenoh-pico/collections/string.h" #undef NDEBUG #include #define MSGS 10 #define SLEEP 1 #define TIMEOUT 15 static const char *PUB_KE = "test/wildcard"; static const char *SUB1_KE = "*/wildcard"; static const char *SUB2_KE = "**/wildcard"; volatile unsigned int sub1_count = 0; volatile unsigned int sub2_count = 0; static void sub_handler_1(z_loaned_sample_t *sample, void *arg) { (void)arg; z_owned_slice_t value; z_bytes_to_slice(z_sample_payload(sample), &value); // Both subscribers should see key "test/wildcard" z_view_string_t k; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k); // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(k)), PUB_KE, strlen(PUB_KE)) == 0); sub1_count++; z_drop(z_move(value)); } static void sub_handler_2(z_loaned_sample_t *sample, void *arg) { (void)arg; z_owned_slice_t value; z_bytes_to_slice(z_sample_payload(sample), &value); z_view_string_t k; z_keyexpr_as_view_string(z_sample_keyexpr(sample), &k); // Flawfinder: ignore [CWE-126] assert(strncmp(z_string_data(z_loan(k)), PUB_KE, strlen(PUB_KE)) == 0); sub2_count++; z_drop(z_move(value)); } int main(int argc, char **argv) { assert(argc == 2); // --- Session 1: publisher --- z_owned_config_t cfg1; z_config_default(&cfg1); zp_config_insert(z_loan_mut(cfg1), Z_CONFIG_CONNECT_KEY, argv[1]); z_owned_session_t s_pub; assert(z_open(&s_pub, z_move(cfg1), NULL) == Z_OK); printf("Publisher session opened\n"); fflush(stdout); // --- Session 2: subscribers --- z_owned_config_t cfg2; z_config_default(&cfg2); zp_config_insert(z_loan_mut(cfg2), Z_CONFIG_CONNECT_KEY, argv[1]); z_owned_session_t s_sub; assert(z_open(&s_sub, z_move(cfg2), NULL) == Z_OK); printf("Subscriber session opened\n"); z_sleep_s(SLEEP); // Declare publisher keyexpr z_view_keyexpr_t pub_ke; assert(z_view_keyexpr_from_str(&pub_ke, PUB_KE) == Z_OK); // Declare publisher z_owned_publisher_t pub; assert(z_declare_publisher(z_loan(s_pub), &pub, z_loan(pub_ke), NULL) == Z_OK); printf("Declared publisher entity id=%zu\n", z_loan(pub)->_id); fflush(stdout); // Declare subscribers on separate session z_view_keyexpr_t sub1_ke, sub2_ke; assert(z_view_keyexpr_from_str(&sub1_ke, SUB1_KE) == Z_OK); assert(z_view_keyexpr_from_str(&sub2_ke, SUB2_KE) == Z_OK); z_owned_closure_sample_t c1, c2; z_closure(&c1, sub_handler_1, NULL, NULL); z_closure(&c2, sub_handler_2, NULL, NULL); z_owned_subscriber_t sub1, sub2; assert(z_declare_subscriber(z_loan(s_sub), &sub1, z_loan(sub1_ke), z_move(c1), NULL) == Z_OK); printf("Declared subscriber on '%s'\n", SUB1_KE); fflush(stdout); assert(z_declare_subscriber(z_loan(s_sub), &sub2, z_loan(sub2_ke), z_move(c2), NULL) == Z_OK); printf("Declared subscriber on '%s'\n", SUB2_KE); fflush(stdout); z_sleep_s(SLEEP); // Publish messages for (unsigned int i = 0; i < MSGS; i++) { char buf[64]; int n = snprintf(buf, sizeof(buf), "hello-%u", i); z_put_options_t opt; z_put_options_default(&opt); opt.congestion_control = Z_CONGESTION_CONTROL_BLOCK; z_owned_bytes_t payload; z_bytes_from_buf(&payload, (uint8_t *)buf, (size_t)n, NULL, NULL); assert(z_put(z_loan(s_pub), z_loan(pub_ke), z_move(payload), &opt) == Z_OK); printf("Published %s to %s\n", buf, PUB_KE); fflush(stdout); } // Wait for both subscribers to receive all messages z_clock_t start = z_clock_now(); while ((sub1_count < MSGS || sub2_count < MSGS) && z_clock_elapsed_s(&start) < TIMEOUT) { printf("Waiting... sub1=%u/%u sub2=%u/%u\n", sub1_count, MSGS, sub2_count, MSGS); fflush(stdout); z_sleep_s(SLEEP); } printf("Final counts: sub1=%u, sub2=%u\n", sub1_count, sub2_count); fflush(stdout); assert(sub1_count == MSGS); assert(sub2_count == MSGS); // Cleanup: undeclare z_drop(z_move(sub1)); z_drop(z_move(sub2)); z_drop(z_move(pub)); z_drop(z_move(s_pub)); z_drop(z_move(s_sub)); return 0; } ================================================ FILE: tools/z_keyexpr_canonizer.c ================================================ // // Copyright (c) 2022 ZettaScale Technology // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 // which is available at https://www.apache.org/licenses/LICENSE-2.0. // // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 // // Contributors: // ZettaScale Zenoh Team, // #include #include #include #include #include #include int main(int argc, char **argv) { if (argc < 2) { printf("USAGE: ./z_keyexpr_canonizer [keyexpr_2 .. keyexpr_N]\n"); printf(" Arguments:\n"); printf(" - Pass any number of key expressions as arguments to obtain their canon forms"); return -1; } char *buffer = NULL; for (int i = 1; i < argc; i++) { size_t len = strlen(argv[i]); buffer = realloc(buffer, len + 1); strncpy(buffer, argv[i], len); buffer[len] = '\0'; zp_keyexpr_canon_status_t status = z_keyexpr_canonize(buffer, &len); switch (status) { case Z_KEYEXPR_CANON_SUCCESS: printf("canon(%s) => %s\r\n", argv[i], buffer); break; case Z_KEYEXPR_CANON_EMPTY_CHUNK: printf( "canon(%s) => Couldn't canonize `%s` because empty chunks are forbidden (as well as leading and " "trailing slashes)\r\n", argv[i], argv[i]); break; case Z_KEYEXPR_CANON_STARS_IN_CHUNK: printf( "canon(%s) => Couldn't canonize `%s` because `*` is only legal when surrounded by `/` or preceded " "by `$`\r\n", argv[i], argv[i]); break; case Z_KEYEXPR_CANON_DOLLAR_AFTER_DOLLAR_OR_STAR: printf("Ccanon(%s) => ouldn't canonize `%s` because `*$` and `$$` are illegal patterns\r\n", argv[i], argv[i]); break; case Z_KEYEXPR_CANON_CONTAINS_SHARP_OR_QMARK: printf("canon(%s) => Couldn't canonize `%s` because `#` and `?` are illegal characters\r\n", argv[i], argv[i]); break; case Z_KEYEXPR_CANON_CONTAINS_UNBOUND_DOLLAR: printf("canon(%s) => Couldn't canonize `%s` because `$` may only be followed by `*`\r\n", argv[i], argv[i]); break; default: assert(false); break; } } return 0; } ================================================ FILE: version.txt ================================================ 1.9.0 ================================================ FILE: zenohpico.pc.in ================================================ prefix=@CMAKE_INSTALL_PREFIX@ Name: @PROJECT_NAME@ Description: @CMAKE_PROJECT_DESCRIPTION@ URL: @CMAKE_PROJECT_HOMEPAGE_URL@ Version: @PROJECT_VERSION@ Cflags: -I${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Libs: -L${prefix}/@CMAKE_INSTALL_LIBDIR@ -lzenohpico@LIBNAME_POSTFIX@ ================================================ FILE: zephyr/CMakeLists.txt ================================================ if(CONFIG_ZENOH_PICO) zephyr_compile_definitions(ZENOH_ZEPHYR) zephyr_include_directories(../include) zephyr_library() function(configure_zenoh_feature config) string(REPLACE CONFIG_ZENOH_PICO Z_FEATURE feature ${config}) if(${config}) zephyr_compile_definitions(${feature}=1) else() zephyr_compile_definitions(${feature}=0) endif() endfunction() configure_zenoh_feature(CONFIG_ZENOH_PICO_LINK_SERIAL) configure_zenoh_feature(CONFIG_ZENOH_PICO_MULTI_THREAD) configure_zenoh_feature(CONFIG_ZENOH_PICO_PUBLICATION) configure_zenoh_feature(CONFIG_ZENOH_PICO_SUBSCRIPTION) configure_zenoh_feature(CONFIG_ZENOH_PICO_QUERY) configure_zenoh_feature(CONFIG_ZENOH_PICO_QUERYABLE) configure_zenoh_feature(CONFIG_ZENOH_PICO_RAWETH_TRANSPORT) configure_zenoh_feature(CONFIG_ZENOH_PICO_LINK_TCP) configure_zenoh_feature(CONFIG_ZENOH_PICO_LINK_UDP_UNICAST) configure_zenoh_feature(CONFIG_ZENOH_PICO_LINK_UDP_MULTICAST) configure_zenoh_feature(CONFIG_ZENOH_PICO_SCOUTING) configure_zenoh_feature(CONFIG_ZENOH_PICO_LINK_WS) file(GLOB_RECURSE Sources "../src/api/*.c" "../src/collections/*.c" "../src/link/*.c" "../src/net/*.c" "../src/protocol/*.c" "../src/session/*.c" "../src/transport/*.c" "../src/utils/*.c" "../src/system/common/*.c" ) file (GLOB Sources_Zephyr "../src/system/zephyr/*.c") list(APPEND Sources ${Sources_Zephyr}) zephyr_library_sources(${Sources}) endif() ================================================ FILE: zephyr/Kconfig.zenoh ================================================ config ZENOH_PICO bool "Zenoh PICO library" help Enable Zenoh pico support if ZENOH_PICO config ZENOH_PICO_LINK_SERIAL bool "Serial Link" help Use serial link config ZENOH_PICO_MULTI_THREAD bool "Multithreading support" help Multithreading support config ZENOH_PICO_PUBLICATION bool "Publication Support" help Publication support config ZENOH_PICO_SUBSCRIPTION bool "Subscription Support" help Subscription Support config ZENOH_PICO_QUERY bool "Query Support" help Query Support config ZENOH_PICO_QUERYABLE bool "Queryable Support" help Queryable Support config ZENOH_PICO_RAWETH_TRANSPORT bool "Raw Ethernet Support" help Raw Ethernet Support config ZENOH_PICO_LINK_TCP bool "TCP Link" help TCP Link config ZENOH_PICO_LINK_UDP_UNICAST bool "UDP Unicast" help UDP Unicast config ZENOH_PICO_LINK_UDP_MULTICAST bool "UDP Multicast" help UDP Multicast config ZENOH_PICO_SCOUTING bool "Scouting UDP" help Scouting UDP config ZENOH_PICO_LINK_WS bool "WS Link" help WS Link endif ================================================ FILE: zephyr/module.yml ================================================ name: zenoh-pico build: cmake: zephyr/ kconfig: zephyr/Kconfig.zenoh